Skip to content

feat(billing): disclose that changing plans on a cancelled subscription resumes it - #14222

Merged
wei-hai merged 14 commits into
mainfrom
wei/be-4782-reactivation-disclosure
Jul 29, 2026
Merged

feat(billing): disclose that changing plans on a cancelled subscription resumes it#14222
wei-hai merged 14 commits into
mainfrom
wei/be-4782-reactivation-disclosure

Conversation

@wei-hai

@wei-hai wei-hai commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

Tells a user that changing plans while their subscription is cancelled will resume it.

Why

A subscription can be cancelled but still active — it runs to the end of the period already paid for. Backend work landing alongside this lets those users change plans, and doing so clears the pending cancellation: the subscription stops ending and starts renewing again.

That is a bigger change than "swap my plan", triggered by what looks like an ordinary plan click. Without a disclosure, someone who deliberately cancelled ends up subscribed indefinitely and never told.

What changes

When the current subscription is cancelled, the transition preview gains a banner above the plan comparison and the primary button becomes "Confirm & reactivate". The copy differs per case because the consequence does:

Case Today What the user needs to notice
Upgrade prorated charge charged now, and it renews instead of ending
Downgrade nothing easiest to miss — no charge, but it now renews at the new price instead of ending
Monthly → annual full year largest charge of the three

Because the downgrade case has no money attached, the copy leads with reactivates, not with a number.

Above the current plan's monthly price the amount renders larger and higher-contrast, and the confirm button is disabled behind an explicit "I understand I'll be charged $X today" checkbox. Standard proration styling renders a $6 upgrade and a $336 annual charge identically, and only one of those deserves a second look.

The load-bearing part

confirm_reactivation is a server-checked flag — the API refuses the change without it, and records it for audit.

An earlier commit on this branch derived it from subscription.isCancelled, which made it true in exactly the circumstances the server demanded it. The check could therefore never fail, and the audit record asserted consent for requests where no disclosure was ever shown. Consent is now computed by the component that actually rendered the banner and emitted on confirm; the composable only forwards it. That distinction is the entire point of the field, so it has a regression test that fails against the old wiring.

Scope notes

  • PricingTableWorkspace.vue is untouched deliberately — a different-tier click while cancelled already routes through this preview, and the "Resubscribe to X" button hits a different endpoint whose label is already explicit.
  • Team and personal paths are both wired.
  • The pre-existing SubscriptionTransitionPreviewWorkspace.test.ts is untouched: its vue-i18n module mock is incompatible with <i18n-t>, so the new tests live in a sibling file.

Verification

pnpm typecheck clean, pnpm lint 0 errors, pnpm knip clean, pnpm format no diff.

Consent is asserted on the request payload, not on component state — a test that only checked the checkbox toggles a local ref would pass with the bug fully intact.

Six Storybook states cover the banner (baseline, upgrade, downgrade, monthly→annual, annual→monthly, above-threshold); rendered screenshots are in a comment below.

Found while reviewing, not fixed here

The credit line reads "Credit from current monthly plan" on an annual→monthly switch, where the current plan is annual. creditFromPlanLabel returns the monthly string for any cadence change and never reads the current subscription's duration. This is on main today, is not touched by this branch, and affects every cadence change rather than just the reactivation path — so it wants its own PR rather than widening this one. It is visible in the annual→monthly screenshot below.

wei-hai added 2 commits July 28, 2026 13:36
…led subscriptions

Relaxing the backend guard on /billing/subscribe to allow plan changes while a
subscription is cancelled means picking a new plan would otherwise silently
clear the pending cancellation and resume auto-renewal with no warning. This
adds a disclosure banner to the plan-transition preview screen so the user
sees they're reactivating, sees the exact charge, and (above their current
monthly price) has to explicitly acknowledge the charge via a checkbox before
confirming.

- SubscriptionTransitionPreviewWorkspace.vue: banner + variant-specific title/
  body/button copy for upgrade/downgrade/duration_change, driven by the
  current subscription's cancelled state; checkbox-gated confirm above the
  current plan's monthly price.
- workspaceApi.ts: adds confirm_reactivation to the subscribe request/options
  so the backend can require explicit confirmation.
- useSubscriptionCheckout.ts: derives confirmReactivation from
  subscription.isCancelled and forwards it on both the personal and team
  subscribe paths.
- main.json: new subscription.preview.reactivation.* copy.
- Tests: new SubscriptionTransitionPreviewWorkspaceReactivation.test.ts for
  the banner; targeted updates to useSubscriptionCheckout.test.ts and
  workspaceApi.test.ts to cover the new flag reaching the request body.
…cription state

confirmReactivation was set to `subscription.value?.isCancelled`, which is
true in exactly the circumstances the server requires the flag — so the
server-side consent check could never fail and the audit trail asserted
consent that was never actually collected.

SubscriptionTransitionPreviewWorkspace now computes consent from its own
state (banner rendered + confirm clicked, checkbox ticked above the charge
threshold) and emits it with `confirm`. useSubscriptionCheckout forwards
that value into `subscribe()` instead of re-deriving it.

Added a regression test that fails against the old wiring: a cancelled
subscription with no disclosure confirmation must not send
confirm_reactivation: true.
@wei-hai
wei-hai requested a review from a team July 28, 2026 20:37
@dosubot dosubot Bot added the size:L This PR changes 100-499 lines, ignoring generated files. label Jul 28, 2026
@github-actions

github-actions Bot commented Jul 28, 2026

Copy link
Copy Markdown

🎨 Storybook: ✅ Built — View Storybook

Details

⏰ Completed at: 07/29/2026, 04:03:14 AM UTC

Links

🎭 Playwright: ✅ 1753 passed, 0 failed · 3 flaky

📊 Browser Reports
  • chromium: View Report (✅ 1732 / ❌ 0 / ⚠️ 3 / ⏭️ 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)

📦 Bundle: 8.15 MB gzip 🔴 +3.7 kB

Details

Summary

  • Raw size: 34.2 MB baseline 34.2 MB — 🔴 +15.9 kB
  • Gzip: 8.15 MB baseline 8.15 MB — 🔴 +3.7 kB
  • Brotli: 5.64 MB baseline 5.63 MB — 🔴 +3.33 kB
  • Bundles: 421 current • 420 baseline • 154 added / 153 removed

Category Glance
Other 🔴 +13.1 kB (12.5 MB) · Utilities & Hooks 🔴 +2.31 kB (371 kB) · Data & Services 🔴 +306 B (3.42 MB) · Graph Workspace 🔴 +88 B (1.28 MB) · Panels & Settings 🔴 +32 B (551 kB) · App Entry Points 🔴 +32 B (3.66 kB) · + 5 more

App Entry Points — 3.66 kB (baseline 3.63 kB) • 🔴 +32 B

Main entry bundles and manifests

File Before After Δ Raw Δ Gzip Δ Brotli
assets/index-DGQdOSxE.js (new) 3.66 kB 🔴 +3.66 kB 🔴 +1.84 kB 🔴 +1.6 kB
assets/index-lKgzZ4cS.js (removed) 3.63 kB 🟢 -3.63 kB 🟢 -1.82 kB 🟢 -1.6 kB

Status: 1 added / 1 removed

Graph Workspace — 1.28 MB (baseline 1.28 MB) • 🔴 +88 B

Graph editor runtime, canvas, workflow orchestration

File Before After Δ Raw Δ Gzip Δ Brotli
assets/GraphView-CVrqTvns.js (new) 1.28 MB 🔴 +1.28 MB 🔴 +276 kB 🔴 +207 kB
assets/GraphView-Bi6AQ3c6.js (removed) 1.28 MB 🟢 -1.28 MB 🟢 -275 kB 🟢 -207 kB

Status: 1 added / 1 removed / 1 unchanged

Views & Navigation — 112 kB (baseline 112 kB) • ⚪ 0 B

Top-level views, pages, and routed surfaces

File Before After Δ Raw Δ Gzip Δ Brotli
assets/CloudSurveyView-DBaWrhna.js (new) 25 kB 🔴 +25 kB 🔴 +6.25 kB 🔴 +5.53 kB
assets/CloudSurveyView-ORaMD6oZ.js (removed) 25 kB 🟢 -25 kB 🟢 -6.25 kB 🟢 -5.54 kB
assets/CloudLoginView-CKBvmE9w.js (new) 11.6 kB 🔴 +11.6 kB 🔴 +3.14 kB 🔴 +2.75 kB
assets/CloudLoginView-DinD2SaU.js (removed) 11.6 kB 🟢 -11.6 kB 🟢 -3.14 kB 🟢 -2.75 kB
assets/CloudSignupView-6j9XEg0q.js (removed) 9.96 kB 🟢 -9.96 kB 🟢 -2.81 kB 🟢 -2.47 kB
assets/CloudSignupView-Cil-Srs8.js (new) 9.96 kB 🔴 +9.96 kB 🔴 +2.81 kB 🔴 +2.47 kB
assets/CloudLayoutView-CgqTVuwp.js (new) 9.54 kB 🔴 +9.54 kB 🔴 +2.35 kB 🔴 +2.02 kB
assets/CloudLayoutView-CIn4iYpv.js (removed) 9.54 kB 🟢 -9.54 kB 🟢 -2.35 kB 🟢 -2.02 kB
assets/UserCheckView-CvcjffaB.js (new) 8.8 kB 🔴 +8.8 kB 🔴 +2.22 kB 🔴 +1.93 kB
assets/UserCheckView-Dst0Z7bI.js (removed) 8.8 kB 🟢 -8.8 kB 🟢 -2.22 kB 🟢 -1.93 kB
assets/WidgetTextPreview-CHhgMb74.js (removed) 6.13 kB 🟢 -6.13 kB 🟢 -2.16 kB 🟢 -1.92 kB
assets/WidgetTextPreview-EhQ59pfH.js (new) 6.13 kB 🔴 +6.13 kB 🔴 +2.16 kB 🔴 +1.92 kB
assets/CloudSubscriptionRedirectView-Bfo5R5Tc.js (removed) 6.06 kB 🟢 -6.06 kB 🟢 -2.26 kB 🟢 -1.97 kB
assets/CloudSubscriptionRedirectView-CeydD_bg.js (new) 6.06 kB 🔴 +6.06 kB 🔴 +2.27 kB 🔴 +1.97 kB
assets/UserSelectView-dIs6e42Z.js (removed) 5.54 kB 🟢 -5.54 kB 🟢 -1.99 kB 🟢 -1.74 kB
assets/UserSelectView-Dl5ymZ5P.js (new) 5.54 kB 🔴 +5.54 kB 🔴 +1.99 kB 🔴 +1.74 kB
assets/CloudForgotPasswordView-BC-CuoiM.js (removed) 5.15 kB 🟢 -5.15 kB 🟢 -1.76 kB 🟢 -1.54 kB
assets/CloudForgotPasswordView-kBit88MZ.js (new) 5.15 kB 🔴 +5.15 kB 🔴 +1.76 kB 🔴 +1.54 kB
assets/CloudAuthTimeoutView-DAIeBkcD.js (new) 4.49 kB 🔴 +4.49 kB 🔴 +1.57 kB 🔴 +1.36 kB
assets/CloudAuthTimeoutView-Du5JN-wR.js (removed) 4.49 kB 🟢 -4.49 kB 🟢 -1.57 kB 🟢 -1.36 kB
assets/OAuthLayoutView-c5957ZeF.js (new) 1.36 kB 🔴 +1.36 kB 🔴 +735 B 🔴 +620 B
assets/OAuthLayoutView-DP-HhRs9.js (removed) 1.36 kB 🟢 -1.36 kB 🟢 -734 B 🟢 -620 B
assets/WidgetTextPreview-B6ABTV3G.js (new) 131 B 🔴 +131 B 🔴 +100 B 🔴 +89 B
assets/WidgetTextPreview-BGPDoLjj.js (removed) 131 B 🟢 -131 B 🟢 -100 B 🟢 -90 B

Status: 12 added / 12 removed / 4 unchanged

Panels & Settings — 551 kB (baseline 551 kB) • 🔴 +32 B

Configuration panels, inspectors, and settings screens

File Before After Δ Raw Δ Gzip Δ Brotli
assets/KeybindingPanel-BPzfDdVA.js (removed) 49.5 kB 🟢 -49.5 kB 🟢 -9.97 kB 🟢 -8.84 kB
assets/KeybindingPanel-iHkhhLvP.js (new) 49.5 kB 🔴 +49.5 kB 🔴 +9.97 kB 🔴 +8.83 kB
assets/SecretsPanel-Ck4P19uA.js (removed) 31.3 kB 🟢 -31.3 kB 🟢 -7.62 kB 🟢 -6.67 kB
assets/SecretsPanel-N9zVylrh.js (new) 31.3 kB 🔴 +31.3 kB 🔴 +7.62 kB 🔴 +6.68 kB
assets/CreditsPanel-BX4SthnO.js (removed) 16 kB 🟢 -16 kB 🟢 -4.66 kB 🟢 -4.09 kB
assets/CreditsPanel-veeiQAaE.js (new) 16 kB 🔴 +16 kB 🔴 +4.66 kB 🔴 +4.09 kB
assets/SubscriptionPanel-DqV2E2XZ.js (new) 11.5 kB 🔴 +11.5 kB 🔴 +3.66 kB 🔴 +3.23 kB
assets/SubscriptionPanel-DPwM0l2T.js (removed) 11.5 kB 🟢 -11.5 kB 🟢 -3.65 kB 🟢 -3.21 kB
assets/AboutPanel-CWkLHCaP.js (removed) 11.2 kB 🟢 -11.2 kB 🟢 -3.06 kB 🟢 -2.74 kB
assets/AboutPanel-DCecs2G6.js (new) 11.2 kB 🔴 +11.2 kB 🔴 +3.06 kB 🔴 +2.74 kB
assets/ExtensionPanel-DOfcRd8q.js (new) 9.24 kB 🔴 +9.24 kB 🔴 +2.54 kB 🔴 +2.25 kB
assets/ExtensionPanel-eYpAxNmv.js (removed) 9.24 kB 🟢 -9.24 kB 🟢 -2.54 kB 🟢 -2.25 kB
assets/ServerConfigPanel-C5yivgE-.js (new) 6.15 kB 🔴 +6.15 kB 🔴 +1.97 kB 🔴 +1.75 kB
assets/ServerConfigPanel-CBHBgPU2.js (removed) 6.15 kB 🟢 -6.15 kB 🟢 -1.97 kB 🟢 -1.75 kB
assets/UserPanel-3DLWaAj7.js (removed) 5.78 kB 🟢 -5.78 kB 🟢 -1.81 kB 🟢 -1.57 kB
assets/UserPanel-bl4bgeYT.js (new) 5.78 kB 🔴 +5.78 kB 🔴 +1.81 kB 🔴 +1.57 kB
assets/refreshRemoteConfig-BkB5-qOA.js (removed) 2.97 kB 🟢 -2.97 kB 🟢 -1.27 kB 🟢 -1.11 kB
assets/refreshRemoteConfig-r4XmcArQ.js (new) 2.97 kB 🔴 +2.97 kB 🔴 +1.27 kB 🔴 +1.11 kB
assets/cloudRemoteConfig-B8FKjXxu.js (new) 988 B 🔴 +988 B 🔴 +538 B 🔴 +446 B
assets/cloudRemoteConfig-C-XyUPop.js (removed) 988 B 🟢 -988 B 🟢 -539 B 🟢 -458 B
assets/refreshRemoteConfig-BS49GUgT.js (new) 110 B 🔴 +110 B 🔴 +89 B 🔴 +89 B
assets/refreshRemoteConfig-C1Jegrjr.js (removed) 110 B 🟢 -110 B 🟢 -89 B 🟢 -84 B

Status: 11 added / 11 removed / 15 unchanged

User & Accounts — 29.1 kB (baseline 29.1 kB) • ⚪ 0 B

Authentication, profile, and account management bundles

File Before After Δ Raw Δ Gzip Δ Brotli
assets/SignUpForm-Bor1wByw.js (new) 12.3 kB 🔴 +12.3 kB 🔴 +4.18 kB 🔴 +3.64 kB
assets/SignUpForm-CAzsfxh8.js (removed) 12.3 kB 🟢 -12.3 kB 🟢 -4.18 kB 🟢 -3.65 kB
assets/auth-D0d8OIyR.js (removed) 3.69 kB 🟢 -3.69 kB 🟢 -1.3 kB 🟢 -1.12 kB
assets/auth-DGIArw_a.js (new) 3.69 kB 🔴 +3.69 kB 🔴 +1.3 kB 🔴 +1.12 kB
assets/usePostAuthRedirect-B5ia5BO3.js (removed) 3.33 kB 🟢 -3.33 kB 🟢 -1.27 kB 🟢 -1.11 kB
assets/usePostAuthRedirect-DcJhmGhn.js (new) 3.33 kB 🔴 +3.33 kB 🔴 +1.27 kB 🔴 +1.11 kB
assets/UpdatePasswordContent-BVYDYT9Y.js (new) 1.91 kB 🔴 +1.91 kB 🔴 +870 B 🔴 +760 B
assets/UpdatePasswordContent-D24F3bkX.js (removed) 1.91 kB 🟢 -1.91 kB 🟢 -873 B 🟢 -758 B
assets/authStore-fAObFLn8.js (removed) 128 B 🟢 -128 B 🟢 -104 B 🟢 -106 B
assets/authStore-pm84-Ez0.js (new) 128 B 🔴 +128 B 🔴 +104 B 🔴 +105 B
assets/workspaceAuthStore-CY82cyc2.js (new) 108 B 🔴 +108 B 🔴 +99 B 🔴 +106 B
assets/workspaceAuthStore-DNs1Hq3p.js (removed) 108 B 🟢 -108 B 🟢 -99 B 🟢 -106 B
assets/auth-Dv8bMB_A.js (removed) 105 B 🟢 -105 B 🟢 -96 B 🟢 -89 B
assets/auth-DVCmHK6d.js (new) 105 B 🔴 +105 B 🔴 +96 B 🔴 +83 B

Status: 7 added / 7 removed / 3 unchanged

Editors & Dialogs — 121 kB (baseline 121 kB) • ⚪ 0 B

Modals, dialogs, drawers, and in-app editors

File Before After Δ Raw Δ Gzip Δ Brotli
assets/ComfyHubPublishDialog-CREdm_1w.js (new) 89.9 kB 🔴 +89.9 kB 🔴 +19.2 kB 🔴 +16.4 kB
assets/ComfyHubPublishDialog-D7JeUeYJ.js (removed) 89.9 kB 🟢 -89.9 kB 🟢 -19.2 kB 🟢 -16.5 kB
assets/useShareDialog-B4alMnP3.js (new) 23.7 kB 🔴 +23.7 kB 🔴 +5.59 kB 🔴 +4.97 kB
assets/useShareDialog-DMM4G_bu.js (removed) 23.7 kB 🟢 -23.7 kB 🟢 -5.58 kB 🟢 -4.95 kB
assets/feedbackDialog-B-MRdYaF.js (new) 4.5 kB 🔴 +4.5 kB 🔴 +1.9 kB 🔴 +1.61 kB
assets/feedbackDialog-KOGahbKZ.js (removed) 4.5 kB 🟢 -4.5 kB 🟢 -1.9 kB 🟢 -1.62 kB
assets/ComfyHubPublishDialog-CaJxF9g7.js (new) 143 B 🔴 +143 B 🔴 +105 B 🔴 +89 B
assets/ComfyHubPublishDialog-CMcEGjsh.js (removed) 143 B 🟢 -143 B 🟢 -105 B 🟢 -90 B
assets/useSubscriptionDialog-Dd4RRCaH.js (new) 108 B 🔴 +108 B 🔴 +102 B 🔴 +96 B
assets/useSubscriptionDialog-v_NXabkm.js (removed) 108 B 🟢 -108 B 🟢 -102 B 🟢 -90 B

Status: 5 added / 5 removed / 1 unchanged

UI Components — 64.8 kB (baseline 64.8 kB) • ⚪ 0 B

Reusable component library chunks

File Before After Δ Raw Δ Gzip Δ Brotli
assets/ComfyQueueButton-CM4u2G0P.js (new) 13.5 kB 🔴 +13.5 kB 🔴 +3.77 kB 🔴 +3.35 kB
assets/ComfyQueueButton-D8hUjdRZ.js (removed) 13.5 kB 🟢 -13.5 kB 🟢 -3.77 kB 🟢 -3.34 kB
assets/useTerminalTabs-CPhwY9Fw.js (removed) 11.8 kB 🟢 -11.8 kB 🟢 -3.72 kB 🟢 -3.29 kB
assets/useTerminalTabs-gfNlBz-6.js (new) 11.8 kB 🔴 +11.8 kB 🔴 +3.72 kB 🔴 +3.29 kB
assets/InviteMembersForm-DD4-Yy1V.js (new) 8.02 kB 🔴 +8.02 kB 🔴 +2.68 kB 🔴 +2.4 kB
assets/InviteMembersForm-DgQXsHds.js (removed) 8.02 kB 🟢 -8.02 kB 🟢 -2.68 kB 🟢 -2.4 kB
assets/SubscribeButton-D1Bev3dK.js (new) 2.18 kB 🔴 +2.18 kB 🔴 +988 B 🔴 +861 B
assets/SubscribeButton-uaksogvj.js (removed) 2.18 kB 🟢 -2.18 kB 🟢 -987 B 🟢 -865 B
assets/cloudFeedbackTopbarButton-D5HfyivD.js (removed) 768 B 🟢 -768 B 🟢 -455 B 🟢 -406 B
assets/cloudFeedbackTopbarButton-DDw-JIFK.js (new) 768 B 🔴 +768 B 🔴 +451 B 🔴 +409 B
assets/ComfyQueueButton-DPe-Awu9.js (removed) 128 B 🟢 -128 B 🟢 -99 B 🟢 -90 B
assets/ComfyQueueButton-DW5gl3A_.js (new) 128 B 🔴 +128 B 🔴 +99 B 🔴 +91 B

Status: 6 added / 6 removed / 8 unchanged

Data & Services — 3.42 MB (baseline 3.42 MB) • 🔴 +306 B

Stores, services, APIs, and repositories

File Before After Δ Raw Δ Gzip Δ Brotli
assets/settingStore-C8FHFMeF.js (new) 3.15 MB 🔴 +3.15 MB 🔴 +730 kB 🔴 +550 kB
assets/settingStore-DJ35jtQK.js (removed) 3.15 MB 🟢 -3.15 MB 🟢 -730 kB 🟢 -550 kB
assets/load3dService-gkF7rEjP.js (new) 132 kB 🔴 +132 kB 🔴 +29.3 kB 🔴 +24.6 kB
assets/load3dService-C0crrUEO.js (removed) 132 kB 🟢 -132 kB 🟢 -29.2 kB 🟢 -24.6 kB
assets/api-DnEb-5YZ.js (new) 91.4 kB 🔴 +91.4 kB 🔴 +25.3 kB 🔴 +21.8 kB
assets/api-CECiQZ80.js (removed) 91.4 kB 🟢 -91.4 kB 🟢 -25.3 kB 🟢 -21.8 kB
assets/workflowShareService-JRvZtXJs.js (removed) 16.5 kB 🟢 -16.5 kB 🟢 -4.92 kB 🟢 -4.36 kB
assets/workflowShareService-Yn67v3jk.js (new) 16.5 kB 🔴 +16.5 kB 🔴 +4.92 kB 🔴 +4.37 kB
assets/keybindingService-BMftTimY.js (new) 7.28 kB 🔴 +7.28 kB 🔴 +1.9 kB 🔴 +1.62 kB
assets/keybindingService-iSSKsz2J.js (removed) 7.28 kB 🟢 -7.28 kB 🟢 -1.9 kB 🟢 -1.62 kB
assets/releaseStore-8cvO0K6C.js (new) 6.77 kB 🔴 +6.77 kB 🔴 +2.06 kB 🔴 +1.8 kB
assets/releaseStore-DroGVtU8.js (removed) 6.77 kB 🟢 -6.77 kB 🟢 -2.06 kB 🟢 -1.8 kB
assets/systemStatsStore-BrJbOmX0.js (removed) 4.99 kB 🟢 -4.99 kB 🟢 -1.77 kB 🟢 -1.5 kB
assets/systemStatsStore-NhSZG2OY.js (new) 4.99 kB 🔴 +4.99 kB 🔴 +1.77 kB 🔴 +1.5 kB
assets/userStore-BnRVmsfi.js (new) 2.42 kB 🔴 +2.42 kB 🔴 +929 B 🔴 +817 B
assets/userStore-mG0o4vnd.js (removed) 2.42 kB 🟢 -2.42 kB 🟢 -930 B 🟢 -820 B
assets/audioService-B-GUoLFA.js (removed) 1.76 kB 🟢 -1.76 kB 🟢 -863 B 🟢 -748 B
assets/audioService-BNM-fvpG.js (new) 1.76 kB 🔴 +1.76 kB 🔴 +863 B 🔴 +747 B
assets/dialogService-DPGDDN5d.js (new) 98 B 🔴 +98 B 🔴 +97 B 🔴 +88 B
assets/dialogService-HGIyBGxo.js (removed) 98 B 🟢 -98 B 🟢 -97 B 🟢 -81 B
assets/releaseStore-DfosMmNe.js (removed) 95 B 🟢 -95 B 🟢 -86 B 🟢 -90 B
assets/releaseStore-DUuobMXV.js (new) 95 B 🔴 +95 B 🔴 +86 B 🔴 +88 B
assets/settingStore-8yBZVNWP.js (new) 95 B 🔴 +95 B 🔴 +86 B 🔴 +84 B
assets/settingStore-DGhY_TM1.js (removed) 95 B 🟢 -95 B 🟢 -86 B 🟢 -82 B
assets/assetsStore-BLdBVmaK.js (removed) 94 B 🟢 -94 B 🟢 -92 B 🟢 -86 B
assets/assetsStore-DnKg5Rbb.js (new) 94 B 🔴 +94 B 🔴 +92 B 🔴 +84 B
assets/api-DXrmaTI1.js (removed) 62 B 🟢 -62 B 🟢 -74 B 🟢 -66 B
assets/api-jXf1NqNZ.js (new) 62 B 🔴 +62 B 🔴 +74 B 🔴 +66 B

Status: 14 added / 14 removed / 3 unchanged

Utilities & Hooks — 371 kB (baseline 369 kB) • 🔴 +2.31 kB

Helpers, composables, and utility bundles

File Before After Δ Raw Δ Gzip Δ Brotli
assets/useConflictDetection-BM91wowP.js (removed) 235 kB 🟢 -235 kB 🟢 -52.5 kB 🟢 -42.7 kB
assets/useConflictDetection-D8QyGkch.js (new) 235 kB 🔴 +235 kB 🔴 +52.5 kB 🔴 +42.8 kB
assets/useLoad3d-B_kfvwps.js (removed) 25.8 kB 🟢 -25.8 kB 🟢 -5.82 kB 🟢 -5.16 kB
assets/useLoad3d-C9m-sgJ_.js (new) 25.8 kB 🔴 +25.8 kB 🔴 +5.82 kB 🔴 +5.16 kB
assets/useLoad3dViewer-Bdwb1I9B.js (new) 21.3 kB 🔴 +21.3 kB 🔴 +5.01 kB 🔴 +4.38 kB
assets/useLoad3dViewer-Hl3Wm2fa.js (removed) 21.3 kB 🟢 -21.3 kB 🟢 -5.01 kB 🟢 -4.38 kB
assets/useDowngradeToPersonal-B3-lL8Z_.js (new) 8.21 kB 🔴 +8.21 kB 🔴 +2.39 kB 🔴 +2.04 kB
assets/useFeatureFlags-D4tq3WMm.js (new) 7.26 kB 🔴 +7.26 kB 🔴 +2.17 kB 🔴 +1.83 kB
assets/useFeatureFlags-ypnvA9ok.js (removed) 7.26 kB 🟢 -7.26 kB 🟢 -2.17 kB 🟢 -1.83 kB
assets/useDowngradeToPersonal-BS0QdsYA.js (removed) 5.9 kB 🟢 -5.9 kB 🟢 -1.8 kB 🟢 -1.57 kB
assets/useExternalLink-C0TRir3V.js (removed) 2.87 kB 🟢 -2.87 kB 🟢 -1.09 kB 🟢 -950 B
assets/useExternalLink-D-A38lal.js (new) 2.87 kB 🔴 +2.87 kB 🔴 +1.09 kB 🔴 +947 B
assets/downloadUtil-CsA1CBwR.js (new) 2.45 kB 🔴 +2.45 kB 🔴 +1.12 kB 🔴 +901 B
assets/downloadUtil-CscHRDGu.js (removed) 2.45 kB 🟢 -2.45 kB 🟢 -1.12 kB 🟢 -900 B
assets/assetPreviewUtil-CZGHKBO8.js (removed) 2.41 kB 🟢 -2.41 kB 🟢 -1.01 kB 🟢 -874 B
assets/assetPreviewUtil-DKJIgw5X.js (new) 2.41 kB 🔴 +2.41 kB 🔴 +1 kB 🔴 +870 B
assets/useUpstreamValue-BUafx8Hw.js (new) 2.04 kB 🔴 +2.04 kB 🔴 +788 B 🔴 +701 B
assets/useUpstreamValue-DQT0dWiT.js (removed) 2.04 kB 🟢 -2.04 kB 🟢 -790 B 🟢 -702 B
assets/useWorkspaceTierLabel-Cn_sYuLB.js (removed) 1.99 kB 🟢 -1.99 kB 🟢 -847 B 🟢 -729 B
assets/useWorkspaceTierLabel-DbhrRSFO.js (new) 1.99 kB 🔴 +1.99 kB 🔴 +845 B 🔴 +731 B
assets/useErrorHandling-DlQl6gZH.js (removed) 1.54 kB 🟢 -1.54 kB 🟢 -648 B 🟢 -548 B
assets/useErrorHandling-DpO7QlUd.js (new) 1.54 kB 🔴 +1.54 kB 🔴 +649 B 🔴 +547 B
assets/subscriptionCheckoutUtil-aIlE-DhS.js (removed) 903 B 🟢 -903 B 🟢 -536 B 🟢 -457 B
assets/subscriptionCheckoutUtil-BDLWqoxy.js (new) 903 B 🔴 +903 B 🔴 +537 B 🔴 +452 B
assets/useSessionCookie-BWnPvEyP.js (removed) 746 B 🟢 -746 B 🟢 -391 B 🟢 -338 B
assets/useSessionCookie-D3kRBhkg.js (new) 746 B 🔴 +746 B 🔴 +392 B 🔴 +336 B
assets/useLoad3d-DkEtLanA.js (removed) 311 B 🟢 -311 B 🟢 -162 B 🟢 -147 B
assets/useLoad3d-iLYSapdw.js (new) 311 B 🔴 +311 B 🔴 +163 B 🔴 +149 B
assets/useSessionCookie-BsenT3L1.js (removed) 101 B 🟢 -101 B 🟢 -86 B 🟢 -83 B
assets/useSessionCookie-CzVQNLDo.js (new) 101 B 🔴 +101 B 🔴 +86 B 🔴 +80 B
assets/useFeatureFlags-Bdg5Dxai.js (new) 98 B 🔴 +98 B 🔴 +85 B 🔴 +80 B
assets/useFeatureFlags-BK4BqWnF.js (removed) 98 B 🟢 -98 B 🟢 -85 B 🟢 -79 B
assets/useLoad3dViewer-Bx9bVOt8.js (removed) 98 B 🟢 -98 B 🟢 -85 B 🟢 -82 B
assets/useLoad3dViewer-DAEqTzUi.js (new) 98 B 🔴 +98 B 🔴 +85 B 🔴 +89 B
assets/useCurrentUser-C1UvrvPZ.js (new) 94 B 🔴 +94 B 🔴 +95 B 🔴 +85 B
assets/useCurrentUser-reo1Yg9a.js (removed) 94 B 🟢 -94 B 🟢 -95 B 🟢 -92 B

Status: 18 added / 18 removed / 17 unchanged

Vendor & Third-Party — 15.7 MB (baseline 15.7 MB) • ⚪ 0 B

External libraries and shared vendor chunks

Status: 16 unchanged

Other — 12.5 MB (baseline 12.5 MB) • 🔴 +13.1 kB

Bundles that do not match a named category

File Before After Δ Raw Δ Gzip Δ Brotli
assets/main-BTxcfCxO.js (new) 197 kB 🔴 +197 kB 🔴 +57.5 kB 🔴 +48.3 kB
assets/main-EI-9geik.js (removed) 195 kB 🟢 -195 kB 🟢 -57.1 kB 🟢 -48 kB
assets/core-Bu52XK0b.js (new) 111 kB 🔴 +111 kB 🔴 +28.7 kB 🔴 +24.3 kB
assets/core-CiZdtwBi.js (removed) 111 kB 🟢 -111 kB 🟢 -28.7 kB 🟢 -24.3 kB
assets/WidgetSelect-BCHmYO5V.js (new) 88 kB 🔴 +88 kB 🔴 +20 kB 🔴 +17.1 kB
assets/WidgetSelect-CJSBSLkV.js (removed) 88 kB 🟢 -88 kB 🟢 -20 kB 🟢 -17.1 kB
assets/Load3D-CljEXows.js (removed) 71.3 kB 🟢 -71.3 kB 🟢 -11.7 kB 🟢 -10 kB
assets/Load3D-DRBDAi_W.js (new) 71.3 kB 🔴 +71.3 kB 🔴 +11.7 kB 🔴 +10 kB
assets/WorkspaceSettingsPanelContent-Crszobc5.js (new) 61.6 kB 🔴 +61.6 kB 🔴 +13.2 kB 🔴 +11.5 kB
assets/WorkspaceSettingsPanelContent-DbPCYJe0.js (removed) 61.6 kB 🟢 -61.6 kB 🟢 -13.2 kB 🟢 -11.5 kB
assets/SubscriptionTransitionPreviewWorkspace-C3zC40Nu.js (new) 53.9 kB 🔴 +53.9 kB 🔴 +11.3 kB 🔴 +9.93 kB
assets/Preview3d-_SZqfGFS.js (new) 50.9 kB 🔴 +50.9 kB 🔴 +8.34 kB 🔴 +7.27 kB
assets/Preview3d-DSC67KS-.js (removed) 50.9 kB 🟢 -50.9 kB 🟢 -8.34 kB 🟢 -7.27 kB
assets/main-DHV44hPn.js (new) 46.7 kB 🔴 +46.7 kB 🔴 +13.8 kB 🔴 +11.9 kB
assets/main-DS-jJnGz.js (removed) 46.7 kB 🟢 -46.7 kB 🟢 -13.8 kB 🟢 -11.9 kB
assets/SubscriptionTransitionPreviewWorkspace-fx3fOg8I.js (removed) 45.4 kB 🟢 -45.4 kB 🟢 -9.65 kB 🟢 -8.44 kB
assets/SubscriptionRequiredDialogContentUnified-Bfdg-GLX.js (new) 42.1 kB 🔴 +42.1 kB 🔴 +9.37 kB 🔴 +8.16 kB
assets/SubscriptionRequiredDialogContentUnified-CGlu4Pe9.js (removed) 42.1 kB 🟢 -42.1 kB 🟢 -9.37 kB 🟢 -8.15 kB
assets/MembersPanelContent-CSKcz6yz.js (new) 41.9 kB 🔴 +41.9 kB 🔴 +8.8 kB 🔴 +7.85 kB
assets/MembersPanelContent-oZmc-PSu.js (removed) 41.9 kB 🟢 -41.9 kB 🟢 -8.8 kB 🟢 -7.86 kB
assets/WidgetBoundingBoxes-0rAq_aLw.js (new) 33.8 kB 🔴 +33.8 kB 🔴 +9.19 kB 🔴 +8.16 kB
assets/WidgetBoundingBoxes-BZn52eBQ.js (removed) 33.8 kB 🟢 -33.8 kB 🟢 -9.19 kB 🟢 -8.16 kB
assets/WidgetPainter-CQ-uPDjI.js (new) 32.7 kB 🔴 +32.7 kB 🔴 +7.9 kB 🔴 +7 kB
assets/WidgetPainter-DZqqoAF4.js (removed) 32.7 kB 🟢 -32.7 kB 🟢 -7.9 kB 🟢 -6.98 kB
assets/Load3dViewerContent-BBeIxXCh.js (removed) 30.8 kB 🟢 -30.8 kB 🟢 -6.29 kB 🟢 -5.46 kB
assets/Load3dViewerContent-BUQRhrbo.js (new) 30.8 kB 🔴 +30.8 kB 🔴 +6.29 kB 🔴 +5.46 kB
assets/HdrViewerContent-B6JD3C-g.js (removed) 29.8 kB 🟢 -29.8 kB 🟢 -8.28 kB 🟢 -7.3 kB
assets/HdrViewerContent-CDe5ZYS9.js (new) 29.8 kB 🔴 +29.8 kB 🔴 +8.28 kB 🔴 +7.3 kB
assets/SubscriptionPanelContentWorkspace-BFnQxX24.js (removed) 26.3 kB 🟢 -26.3 kB 🟢 -6.12 kB 🟢 -5.38 kB
assets/SubscriptionPanelContentWorkspace-Cvd7rSpH.js (new) 26.3 kB 🔴 +26.3 kB 🔴 +6.12 kB 🔴 +5.38 kB
assets/SubscriptionRequiredDialogContentWorkspace-B3hNW67e.js (removed) 24.9 kB 🟢 -24.9 kB 🟢 -5.77 kB 🟢 -5.06 kB
assets/SubscriptionRequiredDialogContentWorkspace-CgHpy1AO.js (new) 24.9 kB 🔴 +24.9 kB 🔴 +5.76 kB 🔴 +5.07 kB
assets/SubscriptionRequiredDialogContent-CKTD2MQw.js (removed) 24.4 kB 🟢 -24.4 kB 🟢 -6 kB 🟢 -5.29 kB
assets/SubscriptionRequiredDialogContent-CObzhftz.js (new) 24.4 kB 🔴 +24.4 kB 🔴 +6 kB 🔴 +5.29 kB
assets/WidgetImageCrop-DIplk0wp.js (new) 23.3 kB 🔴 +23.3 kB 🔴 +5.75 kB 🔴 +5.03 kB
assets/WidgetImageCrop-DQin7y8i.js (removed) 23.3 kB 🟢 -23.3 kB 🟢 -5.75 kB 🟢 -5.04 kB
assets/load3d-C71DfHRb.js (new) 22.2 kB 🔴 +22.2 kB 🔴 +5.44 kB 🔴 +4.69 kB
assets/load3d-DfJPpIjZ.js (removed) 22.2 kB 🟢 -22.2 kB 🟢 -5.43 kB 🟢 -4.69 kB
assets/CurrentUserPopoverWorkspace-BFUI-lJD.js (new) 20.6 kB 🔴 +20.6 kB 🔴 +4.72 kB 🔴 +4.21 kB
assets/CurrentUserPopoverWorkspace-CadLrFmL.js (removed) 20.5 kB 🟢 -20.5 kB 🟢 -4.71 kB 🟢 -4.18 kB
assets/SignInContent-Bdc1gPpl.js (removed) 20.3 kB 🟢 -20.3 kB 🟢 -5.13 kB 🟢 -4.49 kB
assets/SignInContent-Boa8mOJV.js (new) 20.3 kB 🔴 +20.3 kB 🔴 +5.13 kB 🔴 +4.49 kB
assets/WidgetInputNumber-B7_ijtC9.js (new) 19.1 kB 🔴 +19.1 kB 🔴 +4.83 kB 🔴 +4.29 kB
assets/WidgetInputNumber-DUXS5C6W.js (removed) 19.1 kB 🟢 -19.1 kB 🟢 -4.83 kB 🟢 -4.29 kB
assets/CreditsTile-DvcqqIr5.js (new) 17.3 kB 🔴 +17.3 kB 🔴 +4.58 kB 🔴 +4.03 kB
assets/CreditsTile-Dxj0Y6A3.js (removed) 17.2 kB 🟢 -17.2 kB 🟢 -4.57 kB 🟢 -4.03 kB
assets/WidgetRange-BJHO9ifL.js (new) 16.7 kB 🔴 +16.7 kB 🔴 +4.31 kB 🔴 +3.86 kB
assets/WidgetRange-DdwUG31O.js (removed) 16.7 kB 🟢 -16.7 kB 🟢 -4.31 kB 🟢 -3.85 kB
assets/WidgetRecordAudio-B3tNUqeC.js (removed) 16.6 kB 🟢 -16.6 kB 🟢 -4.63 kB 🟢 -4.13 kB
assets/WidgetRecordAudio-BPWUxT0K.js (new) 16.6 kB 🔴 +16.6 kB 🔴 +4.63 kB 🔴 +4.13 kB
assets/WaveAudioPlayer-B3G2hhT0.js (removed) 12.8 kB 🟢 -12.8 kB 🟢 -3.5 kB 🟢 -3.06 kB
assets/WaveAudioPlayer-DreuCMnQ.js (new) 12.8 kB 🔴 +12.8 kB 🔴 +3.49 kB 🔴 +3.06 kB
assets/i18n-Bv_088Jz.js (removed) 12.6 kB 🟢 -12.6 kB 🟢 -3.33 kB 🟢 -2.82 kB
assets/i18n-CYgM55V_.js (new) 12.6 kB 🔴 +12.6 kB 🔴 +3.33 kB 🔴 +2.83 kB
assets/WidgetCurve-BCOaZRl2.js (removed) 11.3 kB 🟢 -11.3 kB 🟢 -3.51 kB 🟢 -3.16 kB
assets/WidgetCurve-Bq1XUkgZ.js (new) 11.3 kB 🔴 +11.3 kB 🔴 +3.5 kB 🔴 +3.16 kB
assets/AudioPreviewPlayer-BdP35Eh7.js (removed) 10.6 kB 🟢 -10.6 kB 🟢 -3.08 kB 🟢 -2.75 kB
assets/AudioPreviewPlayer-BscjsTcW.js (new) 10.6 kB 🔴 +10.6 kB 🔴 +3.08 kB 🔴 +2.75 kB
assets/TeamWorkspacesDialogContent-Bi6-CICI.js (removed) 10.3 kB 🟢 -10.3 kB 🟢 -3 kB 🟢 -2.66 kB
assets/TeamWorkspacesDialogContent-DLECNXdS.js (new) 10.3 kB 🔴 +10.3 kB 🔴 +3 kB 🔴 +2.66 kB
assets/onboardingCloudRoutes-DLTtrO2c.js (new) 9.08 kB 🔴 +9.08 kB 🔴 +2.78 kB 🔴 +2.4 kB
assets/onboardingCloudRoutes-kMFjne1P.js (removed) 9.02 kB 🟢 -9.02 kB 🟢 -2.76 kB 🟢 -2.37 kB
assets/Load3DConfiguration-BZDg4juh.js (removed) 8.97 kB 🟢 -8.97 kB 🟢 -2.64 kB 🟢 -2.33 kB
assets/Load3DConfiguration-CZE7x4N4.js (new) 8.97 kB 🔴 +8.97 kB 🔴 +2.64 kB 🔴 +2.33 kB
assets/SetMemberCreditLimitDialogContent-DjJ95UuC.js (removed) 8.54 kB 🟢 -8.54 kB 🟢 -2.37 kB 🟢 -2.08 kB
assets/SetMemberCreditLimitDialogContent-Qw83iuIN.js (new) 8.54 kB 🔴 +8.54 kB 🔴 +2.37 kB 🔴 +2.06 kB
assets/nodeTemplates-DpIZeCUH.js (removed) 8.33 kB 🟢 -8.33 kB 🟢 -2.88 kB 🟢 -2.54 kB
assets/nodeTemplates-nOsVprdT.js (new) 8.33 kB 🔴 +8.33 kB 🔴 +2.87 kB 🔴 +2.54 kB
assets/NightlySurveyController-Ce-AKSp7.js (removed) 7.56 kB 🟢 -7.56 kB 🟢 -2.59 kB 🟢 -2.28 kB
assets/NightlySurveyController-D9Bk-oTx.js (new) 7.56 kB 🔴 +7.56 kB 🔴 +2.59 kB 🔴 +2.28 kB
assets/WidgetWithControl-BDqPG03u.js (new) 6.4 kB 🔴 +6.4 kB 🔴 +2.61 kB 🔴 +2.3 kB
assets/WidgetWithControl-BcPu8Za4.js (removed) 6.37 kB 🟢 -6.37 kB 🟢 -2.59 kB 🟢 -2.31 kB
assets/tierBenefits-Ba5pLlIX.js (new) 6.26 kB 🔴 +6.26 kB 🔴 +2 kB 🔴 +1.75 kB
assets/tierBenefits-BVU71NtZ.js (removed) 6.26 kB 🟢 -6.26 kB 🟢 -2 kB 🟢 -1.75 kB
assets/CancelSubscriptionDialogContent-7dFHpMu3.js (removed) 5.98 kB 🟢 -5.98 kB 🟢 -2.04 kB 🟢 -1.79 kB
assets/CancelSubscriptionDialogContent-CgNtwZUX.js (new) 5.98 kB 🔴 +5.98 kB 🔴 +2.04 kB 🔴 +1.79 kB
assets/load3dPreviewExtensions-CocWk-5p.js (new) 5.94 kB 🔴 +5.94 kB 🔴 +1.85 kB 🔴 +1.62 kB
assets/load3dPreviewExtensions-mCrBBTME.js (removed) 5.94 kB 🟢 -5.94 kB 🟢 -1.84 kB 🟢 -1.62 kB
assets/main-Dn-4qSFd.js (removed) 5.91 kB 🟢 -5.91 kB 🟢 -1.89 kB 🟢 -1.6 kB
assets/main-rI49HSXX.js (new) 5.91 kB 🔴 +5.91 kB 🔴 +1.89 kB 🔴 +1.61 kB
assets/DowngradeRemoveMembersDialogContent-PX3XKHTv.js (new) 5.66 kB 🔴 +5.66 kB 🔴 +1.92 kB 🔴 +1.67 kB
assets/FreeTierDialogContent-DChenaWE.js (removed) 5.47 kB 🟢 -5.47 kB 🟢 -1.83 kB 🟢 -1.61 kB
assets/FreeTierDialogContent-DZdAMYLB.js (new) 5.47 kB 🔴 +5.47 kB 🔴 +1.83 kB 🔴 +1.61 kB
assets/CreateWorkspaceDialogContent-DJq03N0i.js (removed) 5.19 kB 🟢 -5.19 kB 🟢 -1.83 kB 🟢 -1.58 kB
assets/CreateWorkspaceDialogContent-PDPIVBrd.js (new) 5.19 kB 🔴 +5.19 kB 🔴 +1.83 kB 🔴 +1.58 kB
assets/ChangeMemberRoleDialogContent-C_P_5VJP.js (new) 5.04 kB 🔴 +5.04 kB 🔴 +1.67 kB 🔴 +1.45 kB
assets/ChangeMemberRoleDialogContent-DLM5IJOT.js (removed) 5.04 kB 🟢 -5.04 kB 🟢 -1.67 kB 🟢 -1.45 kB
assets/missingModelDownload-B57k3xGo.js (removed) 5.03 kB 🟢 -5.03 kB 🟢 -1.96 kB 🟢 -1.7 kB
assets/missingModelDownload-L_ucozpz.js (new) 5.03 kB 🔴 +5.03 kB 🔴 +1.95 kB 🔴 +1.7 kB
assets/EditWorkspaceDialogContent-BLV5xeTI.js (removed) 5 kB 🟢 -5 kB 🟢 -1.79 kB 🟢 -1.55 kB
assets/EditWorkspaceDialogContent-CtqG7nem.js (new) 5 kB 🔴 +5 kB 🔴 +1.79 kB 🔴 +1.55 kB
assets/WidgetTextarea-B0QS_XWY.js (removed) 4.86 kB 🟢 -4.86 kB 🟢 -1.9 kB 🟢 -1.65 kB
assets/WidgetTextarea-D4RzbK8-.js (new) 4.86 kB 🔴 +4.86 kB 🔴 +1.9 kB 🔴 +1.66 kB
assets/DowngradeRemoveMembersDialogContent-LPzmy5RG.js (removed) 4.83 kB 🟢 -4.83 kB 🟢 -1.71 kB 🟢 -1.47 kB
assets/saveMesh-BDTZkge4.js (removed) 4.81 kB 🟢 -4.81 kB 🟢 -1.55 kB 🟢 -1.37 kB
assets/saveMesh-C0rhwrko.js (new) 4.81 kB 🔴 +4.81 kB 🔴 +1.55 kB 🔴 +1.37 kB
assets/WorkspacePanelContent-BLwDXvml.js (new) 4.78 kB 🔴 +4.78 kB 🔴 +1.63 kB 🔴 +1.43 kB
assets/WorkspacePanelContent-CYWOLdsp.js (removed) 4.78 kB 🟢 -4.78 kB 🟢 -1.64 kB 🟢 -1.43 kB
assets/InviteMemberDialogContent-BTc_eva2.js (new) 4.75 kB 🔴 +4.75 kB 🔴 +1.61 kB 🔴 +1.4 kB
assets/InviteMemberDialogContent-D1exQc70.js (removed) 4.75 kB 🟢 -4.75 kB 🟢 -1.61 kB 🟢 -1.4 kB
assets/ValueControlPopover-Ca0Jozai.js (new) 4.54 kB 🔴 +4.54 kB 🔴 +1.58 kB 🔴 +1.41 kB
assets/ValueControlPopover-CNPK4jE0.js (removed) 4.54 kB 🟢 -4.54 kB 🟢 -1.58 kB 🟢 -1.42 kB
assets/ApiNodesSignInContent-CjwEOJs4.js (removed) 4.14 kB 🟢 -4.14 kB 🟢 -1.34 kB 🟢 -1.18 kB
assets/ApiNodesSignInContent-Dz34yAER.js (new) 4.14 kB 🔴 +4.14 kB 🔴 +1.34 kB 🔴 +1.18 kB
assets/DeleteWorkspaceDialogContent-D7CT2c1u.js (removed) 3.91 kB 🟢 -3.91 kB 🟢 -1.47 kB 🟢 -1.26 kB
assets/DeleteWorkspaceDialogContent-dvmwiv6f.js (new) 3.91 kB 🔴 +3.91 kB 🔴 +1.47 kB 🔴 +1.26 kB
assets/LeaveWorkspaceDialogContent-CTax3DJ0.js (new) 3.73 kB 🔴 +3.73 kB 🔴 +1.42 kB 🔴 +1.22 kB
assets/LeaveWorkspaceDialogContent-D515BrP8.js (removed) 3.73 kB 🟢 -3.73 kB 🟢 -1.41 kB 🟢 -1.21 kB
assets/RemoveMemberDialogContent-BK7pS83V.js (removed) 3.71 kB 🟢 -3.71 kB 🟢 -1.37 kB 🟢 -1.2 kB
assets/RemoveMemberDialogContent-DJ4oBzix.js (new) 3.71 kB 🔴 +3.71 kB 🔴 +1.37 kB 🔴 +1.18 kB
assets/RevokeInviteDialogContent-C3otbIWT.js (new) 3.62 kB 🔴 +3.62 kB 🔴 +1.38 kB 🔴 +1.2 kB
assets/RevokeInviteDialogContent-CNCPJtuz.js (removed) 3.62 kB 🟢 -3.62 kB 🟢 -1.38 kB 🟢 -1.2 kB
assets/InviteMemberUpsellDialogContent-BhT9on7B.js (removed) 3.51 kB 🟢 -3.51 kB 🟢 -1.26 kB 🟢 -1.1 kB
assets/InviteMemberUpsellDialogContent-Qe1AS0Be.js (new) 3.51 kB 🔴 +3.51 kB 🔴 +1.26 kB 🔴 +1.1 kB
assets/workspaceCheckoutTelemetry-CGr73KZI.js (new) 3.46 kB 🔴 +3.46 kB 🔴 +1.56 kB 🔴 +1.35 kB
assets/workspaceCheckoutTelemetry-DNDn3pjy.js (removed) 3.46 kB 🟢 -3.46 kB 🟢 -1.56 kB 🟢 -1.35 kB
assets/Media3DTop-CAtbQhzc.js (removed) 3.26 kB 🟢 -3.26 kB 🟢 -1.3 kB 🟢 -1.13 kB
assets/Media3DTop-uYcnqyKp.js (new) 3.26 kB 🔴 +3.26 kB 🔴 +1.29 kB 🔴 +1.13 kB
assets/GlobalToast-CH5bvsTN.js (new) 3.05 kB 🔴 +3.05 kB 🔴 +1.25 kB 🔴 +1.11 kB
assets/GlobalToast-DwJTjuvG.js (removed) 3.05 kB 🟢 -3.05 kB 🟢 -1.25 kB 🟢 -1.11 kB
assets/load3dAdvanced-BV7MLYJ2.js (new) 2.87 kB 🔴 +2.87 kB 🔴 +1.13 kB 🔴 +980 B
assets/load3dAdvanced-O8dgOOub.js (removed) 2.87 kB 🟢 -2.87 kB 🟢 -1.13 kB 🟢 -978 B
assets/SubscribeToRun-C1lNIVaI.js (new) 2.45 kB 🔴 +2.45 kB 🔴 +1.06 kB 🔴 +927 B
assets/SubscribeToRun-DtikH1p0.js (removed) 2.45 kB 🟢 -2.45 kB 🟢 -1.06 kB 🟢 -923 B
assets/MediaAudioTop-Cm3KqHV7.js (new) 1.67 kB 🔴 +1.67 kB 🔴 +838 B 🔴 +697 B
assets/MediaAudioTop-D-Yn3kU9.js (removed) 1.67 kB 🟢 -1.67 kB 🟢 -837 B 🟢 -692 B
assets/signInSchema-BVU2FGID.js (removed) 1.6 kB 🟢 -1.6 kB 🟢 -585 B 🟢 -554 B
assets/signInSchema-Dd3GeR-x.js (new) 1.6 kB 🔴 +1.6 kB 🔴 +583 B 🔴 +513 B
assets/comfyCredits-D4x6U-iG.js (new) 1.53 kB 🔴 +1.53 kB 🔴 +563 B 🔴 +493 B
assets/CloudRunButtonWrapper-6hfCOESZ.js (removed) 1.11 kB 🟢 -1.11 kB 🟢 -547 B 🟢 -478 B
assets/CloudRunButtonWrapper-Ccht5XTF.js (new) 1.11 kB 🔴 +1.11 kB 🔴 +547 B 🔴 +478 B
assets/cloudSessionCookie-C7tLIHvA.js (new) 989 B 🔴 +989 B 🔴 +463 B 🔴 +413 B
assets/cloudSessionCookie-CfKHkzuJ.js (removed) 989 B 🟢 -989 B 🟢 -465 B 🟢 -420 B
assets/cloudBadges-CNfvjA57.js (new) 971 B 🔴 +971 B 🔴 +548 B 🔴 +461 B
assets/cloudBadges-D4avUSYt.js (removed) 971 B 🟢 -971 B 🟢 -548 B 🟢 -462 B
assets/Load3DAdvanced-BezMEaF3.js (removed) 813 B 🟢 -813 B 🟢 -452 B 🟢 -405 B
assets/Load3DAdvanced-D7M6676H.js (new) 813 B 🔴 +813 B 🔴 +454 B 🔴 +384 B
assets/nightlyBadges-BL7qZ_cU.js (removed) 462 B 🟢 -462 B 🟢 -306 B 🟢 -251 B
assets/nightlyBadges-CBARVr_h.js (new) 462 B 🔴 +462 B 🔴 +303 B 🔴 +250 B
assets/missingModelDownload-BJ_4sJ_L.js (removed) 228 B 🟢 -228 B 🟢 -151 B 🟢 -129 B
assets/missingModelDownload-DgRef8gT.js (new) 228 B 🔴 +228 B 🔴 +148 B 🔴 +128 B
assets/SubscriptionPanelContentWorkspace-BtJ0QeoK.js (new) 179 B 🔴 +179 B 🔴 +117 B 🔴 +90 B
assets/SubscriptionPanelContentWorkspace-CL94zJ4r.js (removed) 179 B 🟢 -179 B 🟢 -117 B 🟢 -96 B
assets/Load3dViewerContent-CbYiK9Cl.js (new) 137 B 🔴 +137 B 🔴 +103 B 🔴 +91 B
assets/Load3dViewerContent-DWpMk3fP.js (removed) 137 B 🟢 -137 B 🟢 -103 B 🟢 -91 B
assets/Load3DAdvanced-BA1ODVHp.js (removed) 122 B 🟢 -122 B 🟢 -97 B 🟢 -81 B
assets/Load3DAdvanced-DuXDPs4h.js (new) 122 B 🔴 +122 B 🔴 +97 B 🔴 +90 B
assets/WidgetLegacy-CA2MhaSw.js (removed) 117 B 🟢 -117 B 🟢 -106 B 🟢 -94 B
assets/WidgetLegacy-CeT-OqNu.js (new) 117 B 🔴 +117 B 🔴 +106 B 🔴 +100 B
assets/workflowDraftStoreV2-BZgSKAmD.js (new) 112 B 🔴 +112 B 🔴 +101 B 🔴 +108 B
assets/workflowDraftStoreV2-M-WRNMhR.js (removed) 112 B 🟢 -112 B 🟢 -101 B 🟢 -108 B
assets/Load3D-CerT4qVi.js (removed) 98 B 🟢 -98 B 🟢 -89 B 🟢 -78 B
assets/Load3D-Crs2PJnp.js (new) 98 B 🔴 +98 B 🔴 +89 B 🔴 +94 B
assets/i18n-DPhA1ETR.js (new) 97 B 🔴 +97 B 🔴 +92 B 🔴 +101 B
assets/i18n-OZZYq1_9.js (removed) 97 B 🟢 -97 B 🟢 -92 B 🟢 -87 B
assets/changeTracker-BlE9S5ZJ.js (new) 91 B 🔴 +91 B 🔴 +93 B 🔴 +85 B
assets/changeTracker-BPpBC8IX.js (removed) 91 B 🟢 -91 B 🟢 -93 B 🟢 -86 B

Status: 79 added / 78 removed / 199 unchanged

⚡ Performance Report

canvas-idle: · 60.0 avg FPS · 59.9 P5 FPS ✅ (target: ≥52) · 0ms TBT · 68.3 MB heap
canvas-mouse-sweep: · 60.0 avg FPS · 59.9 P5 FPS ✅ (target: ≥52) · 0ms TBT · 56.0 MB heap
canvas-zoom-sweep: · 60.0 avg FPS · 59.9 P5 FPS ✅ (target: ≥52) · 0ms TBT · 71.6 MB heap
dom-widget-clipping: · 60.0 avg FPS · 59.5 P5 FPS ✅ (target: ≥52) · 0ms TBT · 53.1 MB heap
large-graph-idle: · 60.0 avg FPS · 59.5 P5 FPS ✅ (target: ≥52) · 0ms TBT · 62.1 MB heap
large-graph-pan: · 60.0 avg FPS · 59.7 P5 FPS ✅ (target: ≥52) · 0ms TBT · 61.9 MB heap
large-graph-zoom: · 60.0 avg FPS · 59.7 P5 FPS ✅ (target: ≥52) · 0ms TBT · 56.2 MB heap
minimap-idle: · 60.0 avg FPS · 59.9 P5 FPS ✅ (target: ≥52) · 0ms TBT · 64.5 MB heap
subgraph-dom-widget-clipping: · 60.0 avg FPS · 59.5 P5 FPS ✅ (target: ≥52) · 0ms TBT · 53.6 MB heap
subgraph-idle: · 60.0 avg FPS · 59.5 P5 FPS ✅ (target: ≥52) · 0ms TBT · 68.0 MB heap
subgraph-mouse-sweep: · 60.0 avg FPS · 59.9 P5 FPS ✅ (target: ≥52) · 0ms TBT · 46.0 MB heap
subgraph-transition-enter: · 60.0 avg FPS · 59.5 P5 FPS ✅ (target: ≥52) · 135ms TBT · 93.0 MB heap
viewport-pan-sweep: · 60.0 avg FPS · 59.9 P5 FPS ✅ (target: ≥52) · 0ms TBT · 62.1 MB heap
vue-large-graph-idle: · 57.1 avg FPS · 59.5 P5 FPS ✅ (target: ≥52) · 0ms TBT · 158.7 MB heap
vue-large-graph-pan: · 54.6 avg FPS · 59.5 P5 FPS ✅ (target: ≥52) · 83ms TBT · 158.7 MB heap
workflow-execution: · 60.0 avg FPS · 59.7 P5 FPS ✅ (target: ≥52) · 0ms TBT · 57.8 MB heap

⚠️ 5 regressions detected

Show regressions
Metric Baseline PR (median) Δ Sig
canvas-idle: task duration 445ms 518ms +16% ⚠️ z=4.0
large-graph-idle: task duration 608ms 662ms +9% ⚠️ z=2.2
large-graph-pan: task duration 1123ms 1300ms +16% ⚠️ z=5.1
minimap-idle: task duration 612ms 660ms +8% ⚠️ z=2.8
subgraph-idle: task duration 422ms 439ms +4% ⚠️ z=2.2
All metrics
Metric Baseline PR (median) Δ Sig
canvas-idle: avg frame time 17ms 17ms +0% z=-0.1
canvas-idle: p95 frame time 17ms 17ms -1%
canvas-idle: layout duration 0ms 0ms +0%
canvas-idle: style recalc duration 6ms 8ms +28% z=-3.2
canvas-idle: layout count 0 0 +0%
canvas-idle: style recalc count 9 9 -6% z=-4.7
canvas-idle: task duration 445ms 518ms +16% ⚠️ z=4.0
canvas-idle: script duration 16ms 18ms +10% z=-3.5
canvas-idle: TBT 0ms 0ms +0%
canvas-idle: heap used 68.0 MB 68.3 MB +0%
canvas-idle: DOM nodes 18 17 -6% z=-4.4
canvas-idle: event listeners 6 4 -33% z=-1.6
canvas-mouse-sweep: avg frame time 17ms 17ms +0% z=-0.9
canvas-mouse-sweep: p95 frame time 17ms 17ms -1%
canvas-mouse-sweep: layout duration 3ms 3ms +14% z=-0.7
canvas-mouse-sweep: style recalc duration 33ms 38ms +15% z=-1.5
canvas-mouse-sweep: layout count 12 12 +0%
canvas-mouse-sweep: style recalc count 72 75 +4% z=-1.6
canvas-mouse-sweep: task duration 815ms 847ms +4% z=-0.3
canvas-mouse-sweep: script duration 105ms 124ms +19% z=-1.7
canvas-mouse-sweep: TBT 0ms 0ms +0%
canvas-mouse-sweep: heap used 49.9 MB 56.0 MB +12%
canvas-mouse-sweep: DOM nodes -275 -109 -61% z=-66.0
canvas-mouse-sweep: event listeners -148 -72 -51% z=-19.3
canvas-zoom-sweep: avg frame time 17ms 17ms +0% z=0.5
canvas-zoom-sweep: p95 frame time 17ms 17ms +0%
canvas-zoom-sweep: layout duration 1ms 1ms -32% z=-3.0
canvas-zoom-sweep: style recalc duration 15ms 16ms +8% z=-1.9
canvas-zoom-sweep: layout count 6 6 +0%
canvas-zoom-sweep: style recalc count 32 32 -2% z=0.5
canvas-zoom-sweep: task duration 348ms 356ms +2% z=1.3
canvas-zoom-sweep: script duration 18ms 18ms -1% z=-3.0
canvas-zoom-sweep: TBT 0ms 0ms +0%
canvas-zoom-sweep: heap used 71.7 MB 71.6 MB -0%
canvas-zoom-sweep: DOM nodes 76 78 +2% z=-2.2
canvas-zoom-sweep: event listeners 19 19 +0% z=-0.9
dom-widget-clipping: avg frame time 17ms 17ms +0% z=0.1
dom-widget-clipping: p95 frame time 17ms 17ms +1%
dom-widget-clipping: layout duration 0ms 0ms +0%
dom-widget-clipping: style recalc duration 6ms 7ms +17% z=-3.0
dom-widget-clipping: layout count 0 0 +0%
dom-widget-clipping: style recalc count 12 11 -8% z=-4.2
dom-widget-clipping: task duration 340ms 366ms +8% z=0.1
dom-widget-clipping: script duration 49ms 58ms +19% z=-3.0
dom-widget-clipping: TBT 0ms 0ms +0%
dom-widget-clipping: heap used 53.0 MB 53.1 MB +0%
dom-widget-clipping: DOM nodes 20 18 -10% z=-2.9
dom-widget-clipping: event listeners 0 2 variance too high
large-graph-idle: avg frame time 17ms 17ms +0% z=-0.2
large-graph-idle: p95 frame time 17ms 17ms +0%
large-graph-idle: layout duration 0ms 0ms +0%
large-graph-idle: style recalc duration 6ms 8ms +21% z=-4.2
large-graph-idle: layout count 0 0 +0%
large-graph-idle: style recalc count 9 9 -6% z=-9.9
large-graph-idle: task duration 608ms 662ms +9% ⚠️ z=2.2
large-graph-idle: script duration 81ms 106ms +31% z=0.3
large-graph-idle: TBT 0ms 0ms +0%
large-graph-idle: heap used 62.7 MB 62.1 MB -1%
large-graph-idle: DOM nodes -276 -276 +0% z=-332.7
large-graph-idle: event listeners -144 -144 +0% z=-27.9
large-graph-pan: avg frame time 17ms 17ms +0% z=0.3
large-graph-pan: p95 frame time 17ms 17ms +0%
large-graph-pan: layout duration 0ms 0ms +0%
large-graph-pan: style recalc duration 12ms 14ms +10% z=-4.4
large-graph-pan: layout count 0 0 +0%
large-graph-pan: style recalc count 68 69 +1% z=-1.7
large-graph-pan: task duration 1123ms 1300ms +16% ⚠️ z=5.1
large-graph-pan: script duration 348ms 445ms +28% z=1.8
large-graph-pan: TBT 0ms 0ms +0%
large-graph-pan: heap used 59.8 MB 61.9 MB +4%
large-graph-pan: DOM nodes -277 -277 +0% z=-179.4
large-graph-pan: event listeners -144 -143 -1% z=-178.6
large-graph-zoom: avg frame time 17ms 17ms +0%
large-graph-zoom: p95 frame time 17ms 17ms +0%
large-graph-zoom: layout duration 8ms 7ms -15%
large-graph-zoom: style recalc duration 15ms 15ms -4%
large-graph-zoom: layout count 60 60 +0%
large-graph-zoom: style recalc count 65 66 +2%
large-graph-zoom: task duration 1325ms 1488ms +12%
large-graph-zoom: script duration 424ms 521ms +23%
large-graph-zoom: TBT 0ms 0ms +0%
large-graph-zoom: heap used 57.2 MB 56.2 MB -2%
large-graph-zoom: DOM nodes -279 -280 +0%
large-graph-zoom: event listeners -148 -148 +0%
minimap-idle: avg frame time 17ms 17ms -0% z=0.1
minimap-idle: p95 frame time 17ms 17ms -0%
minimap-idle: layout duration 0ms 0ms +0%
minimap-idle: style recalc duration 6ms 7ms +18% z=-2.7
minimap-idle: layout count 0 0 +0%
minimap-idle: style recalc count 9 9 -6% z=-1.6
minimap-idle: task duration 612ms 660ms +8% ⚠️ z=2.8
minimap-idle: script duration 79ms 106ms +33% z=0.7
minimap-idle: TBT 0ms 0ms +0%
minimap-idle: heap used 63.9 MB 64.5 MB +1%
minimap-idle: DOM nodes -276 -277 +0% z=-216.2
minimap-idle: event listeners -144 -143 -1% z=-223.5
subgraph-dom-widget-clipping: avg frame time 17ms 17ms +0% z=0.1
subgraph-dom-widget-clipping: p95 frame time 17ms 17ms +1%
subgraph-dom-widget-clipping: layout duration 0ms 0ms +0%
subgraph-dom-widget-clipping: style recalc duration 10ms 11ms +5% z=-2.2
subgraph-dom-widget-clipping: layout count 0 0 +0%
subgraph-dom-widget-clipping: style recalc count 48 48 -1% z=-0.8
subgraph-dom-widget-clipping: task duration 377ms 389ms +3% z=0.6
subgraph-dom-widget-clipping: script duration 110ms 116ms +5% z=-2.0
subgraph-dom-widget-clipping: TBT 0ms 0ms +0%
subgraph-dom-widget-clipping: heap used 53.6 MB 53.6 MB +0%
subgraph-dom-widget-clipping: DOM nodes 22 21 -5% z=-1.1
subgraph-dom-widget-clipping: event listeners 6 8 +33% z=-1.4
subgraph-idle: avg frame time 17ms 17ms +0% z=0.4
subgraph-idle: p95 frame time 17ms 17ms -0%
subgraph-idle: layout duration 0ms 0ms +0%
subgraph-idle: style recalc duration 7ms 9ms +28% z=-1.8
subgraph-idle: layout count 0 0 +0%
subgraph-idle: style recalc count 11 11 -5% z=-0.6
subgraph-idle: task duration 422ms 439ms +4% ⚠️ z=2.2
subgraph-idle: script duration 14ms 13ms -7% z=-2.7
subgraph-idle: TBT 0ms 0ms +0%
subgraph-idle: heap used 68.2 MB 68.0 MB -0%
subgraph-idle: DOM nodes 22 21 -5% z=-0.5
subgraph-idle: event listeners 4 4 +0% variance too high
subgraph-mouse-sweep: avg frame time 17ms 17ms +0% z=0.4
subgraph-mouse-sweep: p95 frame time 17ms 17ms +0%
subgraph-mouse-sweep: layout duration 4ms 4ms -2% z=-2.2
subgraph-mouse-sweep: style recalc duration 33ms 36ms +8% z=-2.0
subgraph-mouse-sweep: layout count 16 16 +0%
subgraph-mouse-sweep: style recalc count 76 77 +1% z=-1.6
subgraph-mouse-sweep: task duration 714ms 782ms +10% z=0.2
subgraph-mouse-sweep: script duration 76ms 89ms +17% z=-1.8
subgraph-mouse-sweep: TBT 0ms 0ms +0%
subgraph-mouse-sweep: heap used 59.5 MB 46.0 MB -23%
subgraph-mouse-sweep: DOM nodes 61 -273 -547% z=-151.9
subgraph-mouse-sweep: event listeners 4 -148 -3800% variance too high
subgraph-transition-enter: avg frame time 17ms 17ms +0%
subgraph-transition-enter: p95 frame time 17ms 17ms +1%
subgraph-transition-enter: layout duration 10ms 13ms +32%
subgraph-transition-enter: style recalc duration 26ms 31ms +18%
subgraph-transition-enter: layout count 15 15 +0%
subgraph-transition-enter: style recalc count 19 19 +0%
subgraph-transition-enter: task duration 830ms 978ms +18%
subgraph-transition-enter: script duration 31ms 36ms +15%
subgraph-transition-enter: TBT 118ms 135ms +14%
subgraph-transition-enter: heap used 92.4 MB 93.0 MB +1%
subgraph-transition-enter: DOM nodes 13673 13673 +0%
subgraph-transition-enter: event listeners 2369 2371 +0%
viewport-pan-sweep: avg frame time 17ms 17ms +0%
viewport-pan-sweep: p95 frame time 17ms 17ms +0%
viewport-pan-sweep: layout duration 0ms 0ms +0%
viewport-pan-sweep: style recalc duration 39ms 34ms -12%
viewport-pan-sweep: layout count 0 0 +0%
viewport-pan-sweep: style recalc count 249 249 +0%
viewport-pan-sweep: task duration 3996ms 4349ms +9%
viewport-pan-sweep: script duration 1161ms 1351ms +16%
viewport-pan-sweep: TBT 0ms 0ms +0%
viewport-pan-sweep: heap used 66.0 MB 62.1 MB -6%
viewport-pan-sweep: DOM nodes -276 -276 -0%
viewport-pan-sweep: event listeners -128 -128 +0%
vue-large-graph-idle: avg frame time 17ms 17ms +2%
vue-large-graph-idle: p95 frame time 17ms 17ms -0%
vue-large-graph-idle: layout duration 0ms 0ms +0%
vue-large-graph-idle: style recalc duration 0ms 0ms +0%
vue-large-graph-idle: layout count 0 0 +0%
vue-large-graph-idle: style recalc count 0 0 +0%
vue-large-graph-idle: task duration 13916ms 16396ms +18%
vue-large-graph-idle: script duration 504ms 538ms +7%
vue-large-graph-idle: TBT 0ms 0ms +0%
vue-large-graph-idle: heap used 158.2 MB 158.7 MB +0%
vue-large-graph-idle: DOM nodes -8312 -8312 +0%
vue-large-graph-idle: event listeners -16389 -16387 -0%
vue-large-graph-pan: avg frame time 17ms 18ms +6%
vue-large-graph-pan: p95 frame time 17ms 17ms +0%
vue-large-graph-pan: layout duration 0ms 0ms +0%
vue-large-graph-pan: style recalc duration 17ms 14ms -13%
vue-large-graph-pan: layout count 0 0 +0%
vue-large-graph-pan: style recalc count 115 136 +18%
vue-large-graph-pan: task duration 17149ms 19945ms +16%
vue-large-graph-pan: script duration 829ms 855ms +3%
vue-large-graph-pan: TBT 20ms 83ms +315%
vue-large-graph-pan: heap used 159.5 MB 158.7 MB -1%
vue-large-graph-pan: DOM nodes -8312 -8312 +0%
vue-large-graph-pan: event listeners -16383 -16384 +0%
workflow-execution: avg frame time 17ms 17ms -0% z=0.1
workflow-execution: p95 frame time 17ms 17ms -0%
workflow-execution: layout duration 1ms 1ms +1% z=-7.0
workflow-execution: style recalc duration 15ms 17ms +13% z=-3.2
workflow-execution: layout count 3 3 -17% z=-4.5
workflow-execution: style recalc count 13 10 -23% z=-3.8
workflow-execution: task duration 107ms 95ms -11% z=-2.6
workflow-execution: script duration 9ms 9ms +1% z=-6.9
workflow-execution: TBT 0ms 0ms +0%
workflow-execution: heap used 48.8 MB 57.8 MB +19%
workflow-execution: DOM nodes 121 115 -5% z=-6.5
workflow-execution: event listeners 65 49 -25% z=-0.7
Historical variance (last 15 runs)
Metric μ σ CV
canvas-idle: avg frame time 17ms 0ms 0.0%
canvas-idle: layout duration 0ms 0ms 0.0%
canvas-idle: style recalc duration 11ms 1ms 8.2%
canvas-idle: layout count 0 0 0.0%
canvas-idle: style recalc count 11 1 5.0%
canvas-idle: task duration 395ms 31ms 7.9%
canvas-idle: script duration 25ms 2ms 8.8%
canvas-idle: TBT 0ms 0ms 0.0%
canvas-idle: DOM nodes 23 1 5.6%
canvas-idle: event listeners 12 5 40.9%
canvas-mouse-sweep: avg frame time 17ms 0ms 0.0%
canvas-mouse-sweep: layout duration 4ms 0ms 5.4%
canvas-mouse-sweep: style recalc duration 43ms 3ms 7.4%
canvas-mouse-sweep: layout count 12 0 0.0%
canvas-mouse-sweep: style recalc count 79 2 3.0%
canvas-mouse-sweep: task duration 865ms 58ms 6.7%
canvas-mouse-sweep: script duration 136ms 6ms 4.8%
canvas-mouse-sweep: TBT 0ms 0ms 0.0%
canvas-mouse-sweep: DOM nodes 62 3 4.2%
canvas-mouse-sweep: event listeners 8 4 49.4%
canvas-zoom-sweep: avg frame time 17ms 0ms 0.0%
canvas-zoom-sweep: layout duration 1ms 0ms 7.0%
canvas-zoom-sweep: style recalc duration 19ms 2ms 8.0%
canvas-zoom-sweep: layout count 6 0 0.0%
canvas-zoom-sweep: style recalc count 31 0 1.5%
canvas-zoom-sweep: task duration 327ms 23ms 7.1%
canvas-zoom-sweep: script duration 27ms 3ms 11.1%
canvas-zoom-sweep: TBT 0ms 0ms 0.0%
canvas-zoom-sweep: DOM nodes 79 1 1.0%
canvas-zoom-sweep: event listeners 24 5 21.8%
dom-widget-clipping: avg frame time 17ms 0ms 0.0%
dom-widget-clipping: layout duration 0ms 0ms 0.0%
dom-widget-clipping: style recalc duration 10ms 1ms 8.0%
dom-widget-clipping: layout count 0 0 0.0%
dom-widget-clipping: style recalc count 13 0 3.8%
dom-widget-clipping: task duration 365ms 16ms 4.5%
dom-widget-clipping: script duration 68ms 3ms 4.8%
dom-widget-clipping: TBT 0ms 0ms 0.0%
dom-widget-clipping: DOM nodes 22 1 6.4%
dom-widget-clipping: event listeners 8 6 81.2%
large-graph-idle: avg frame time 17ms 0ms 0.0%
large-graph-idle: layout duration 0ms 0ms 0.0%
large-graph-idle: style recalc duration 12ms 1ms 8.6%
large-graph-idle: layout count 0 0 0.0%
large-graph-idle: style recalc count 12 0 2.7%
large-graph-idle: task duration 542ms 54ms 10.0%
large-graph-idle: script duration 102ms 11ms 10.3%
large-graph-idle: TBT 0ms 0ms 0.0%
large-graph-idle: DOM nodes 25 1 3.7%
large-graph-idle: event listeners 26 6 23.2%
large-graph-pan: avg frame time 17ms 0ms 0.0%
large-graph-pan: layout duration 0ms 0ms 0.0%
large-graph-pan: style recalc duration 17ms 1ms 4.6%
large-graph-pan: layout count 0 0 0.0%
large-graph-pan: style recalc count 70 1 0.9%
large-graph-pan: task duration 1082ms 43ms 4.0%
large-graph-pan: script duration 408ms 20ms 4.8%
large-graph-pan: TBT 0ms 0ms 0.0%
large-graph-pan: DOM nodes 19 2 8.7%
large-graph-pan: event listeners 5 1 16.8%
minimap-idle: avg frame time 17ms 0ms 0.0%
minimap-idle: layout duration 0ms 0ms 0.0%
minimap-idle: style recalc duration 10ms 1ms 8.6%
minimap-idle: layout count 0 0 0.0%
minimap-idle: style recalc count 10 1 7.1%
minimap-idle: task duration 527ms 47ms 9.0%
minimap-idle: script duration 98ms 10ms 10.1%
minimap-idle: TBT 0ms 0ms 0.0%
minimap-idle: DOM nodes 19 1 7.1%
minimap-idle: event listeners 5 1 14.4%
subgraph-dom-widget-clipping: avg frame time 17ms 0ms 0.0%
subgraph-dom-widget-clipping: layout duration 0ms 0ms 0.0%
subgraph-dom-widget-clipping: style recalc duration 13ms 1ms 7.4%
subgraph-dom-widget-clipping: layout count 0 0 0.0%
subgraph-dom-widget-clipping: style recalc count 48 1 1.2%
subgraph-dom-widget-clipping: task duration 378ms 18ms 4.9%
subgraph-dom-widget-clipping: script duration 128ms 6ms 4.9%
subgraph-dom-widget-clipping: TBT 0ms 0ms 0.0%
subgraph-dom-widget-clipping: DOM nodes 22 1 5.0%
subgraph-dom-widget-clipping: event listeners 16 6 36.0%
subgraph-idle: avg frame time 17ms 0ms 0.0%
subgraph-idle: layout duration 0ms 0ms 0.0%
subgraph-idle: style recalc duration 10ms 1ms 7.5%
subgraph-idle: layout count 0 0 0.0%
subgraph-idle: style recalc count 11 1 6.0%
subgraph-idle: task duration 370ms 31ms 8.5%
subgraph-idle: script duration 20ms 3ms 13.2%
subgraph-idle: TBT 0ms 0ms 0.0%
subgraph-idle: DOM nodes 22 1 6.9%
subgraph-idle: event listeners 10 7 64.5%
subgraph-mouse-sweep: avg frame time 17ms 0ms 0.0%
subgraph-mouse-sweep: layout duration 5ms 0ms 6.8%
subgraph-mouse-sweep: style recalc duration 42ms 3ms 7.8%
subgraph-mouse-sweep: layout count 16 0 0.0%
subgraph-mouse-sweep: style recalc count 80 2 2.4%
subgraph-mouse-sweep: task duration 766ms 69ms 9.0%
subgraph-mouse-sweep: script duration 101ms 7ms 6.5%
subgraph-mouse-sweep: TBT 0ms 0ms 0.0%
subgraph-mouse-sweep: DOM nodes 67 2 3.3%
subgraph-mouse-sweep: event listeners 8 4 52.6%
workflow-execution: avg frame time 17ms 0ms 0.0%
workflow-execution: layout duration 2ms 0ms 9.4%
workflow-execution: style recalc duration 24ms 2ms 9.1%
workflow-execution: layout count 5 1 11.0%
workflow-execution: style recalc count 18 2 11.5%
workflow-execution: task duration 123ms 11ms 8.8%
workflow-execution: script duration 29ms 3ms 10.2%
workflow-execution: TBT 0ms 0ms 0.0%
workflow-execution: DOM nodes 161 7 4.4%
workflow-execution: event listeners 52 4 8.4%
Trend (last 15 commits on main)
Metric Trend Dir Latest
canvas-idle: avg frame time ▆▃▆▁▆▃▆█▆▆▄▃▃▄▃ ➡️ 17ms
canvas-idle: p95 frame time ➡️ NaNms
canvas-idle: layout duration ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0ms
canvas-idle: style recalc duration ▇▇▆▆▃█▄▃▄▃▇▄▁▆▇ ➡️ 11ms
canvas-idle: layout count ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0
canvas-idle: style recalc count █▃▅▂▅▆▃▁▂▁▂▅▆▅▆ ➡️ 12
canvas-idle: task duration ▃▃▃▆▂▃▃▅▆▂█▃▁▃▃ ➡️ 391ms
canvas-idle: script duration ▄▃▅▇▂▅▃▆▇▅█▄▁▅▆ ➡️ 27ms
canvas-idle: TBT ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0ms
canvas-idle: heap used ➡️ NaN MB
canvas-idle: DOM nodes █▇▆▅▃▇▃▁▂▂▅▆▆▆▇ ➡️ 24
canvas-idle: event listeners ▅█▅▄▁▅▁▁▁▄▅▅▁▅▄ 📉 11
canvas-mouse-sweep: avg frame time ▆█▆▃▁▃▁▆▆▁▃▆▆▃▃ ➡️ 17ms
canvas-mouse-sweep: p95 frame time ➡️ NaNms
canvas-mouse-sweep: layout duration ▁▃▂▄▁▂▁▃▆▂█▇▆▄▃ ➡️ 4ms
canvas-mouse-sweep: style recalc duration ▄▄▂▄▁▂▃▃▅▄█▆▂▄▄ ➡️ 43ms
canvas-mouse-sweep: layout count ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 12
canvas-mouse-sweep: style recalc count █▅▄▃▂▂▁▄▄▅▆▅▂▇▄ ➡️ 79
canvas-mouse-sweep: task duration █▆▄▂▂▃▂▄▄▅█▆▁▆▄ ➡️ 868ms
canvas-mouse-sweep: script duration ▄▅▄▆▄▆▆▆▅▅█▆▁▅▆ ➡️ 139ms
canvas-mouse-sweep: TBT ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0ms
canvas-mouse-sweep: heap used ➡️ NaN MB
canvas-mouse-sweep: DOM nodes █▅▃▃▁▂▂▃▂▄▆▅▃▅▅ ➡️ 64
canvas-mouse-sweep: event listeners █▁▁▁▁▁▇▁▁▁██▇▁█ 📈 13
canvas-zoom-sweep: avg frame time ▅▅█▄▅▁▁▁▅▁▁▅▄▅▁ ➡️ 17ms
canvas-zoom-sweep: p95 frame time ➡️ NaNms
canvas-zoom-sweep: layout duration ▆▅▅▄▁▁█▅▃▅▇▆▁▂▆ ➡️ 1ms
canvas-zoom-sweep: style recalc duration ▆▅▄▆▅▃█▆▇▅▇▄▁▃▅ ➡️ 20ms
canvas-zoom-sweep: layout count ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 6
canvas-zoom-sweep: style recalc count ▁▁▃▄▆▃▆█▄▄▆▁▆▁▆ ➡️ 32
canvas-zoom-sweep: task duration ▄▂▁▇▂▂▄▅▆▃█▄▁▁▅ ➡️ 338ms
canvas-zoom-sweep: script duration ▃▃▂▇▂▂▅▇▆▅█▄▁▂▆ ➡️ 30ms
canvas-zoom-sweep: TBT ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0ms
canvas-zoom-sweep: heap used ➡️ NaN MB
canvas-zoom-sweep: DOM nodes ▄▃▁▅█▁▃▆▄▅▅▃▃▄▃ ➡️ 79
canvas-zoom-sweep: event listeners ▁▁▂▅█▂▁▅▁▅▅▄▁▅▁ ➡️ 19
dom-widget-clipping: avg frame time ▂▄▅▅▂▄█▇▅▇▇▅▅▁▇ ➡️ 17ms
dom-widget-clipping: p95 frame time ➡️ NaNms
dom-widget-clipping: layout duration ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0ms
dom-widget-clipping: style recalc duration ▆▆▂▆▄▃██▄▁▆▇▆▃▅ ➡️ 10ms
dom-widget-clipping: layout count ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0
dom-widget-clipping: style recalc count ▇█▅█▅▄█▇▇▁▇▄▇▂▅ ➡️ 13
dom-widget-clipping: task duration ▃▃▁▅▄▃▅▆▅▂▇█▁▅▅ ➡️ 371ms
dom-widget-clipping: script duration ▅▄▄▆▆▅▇▇▆▃█▇▁▇▇ ➡️ 71ms
dom-widget-clipping: TBT ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0ms
dom-widget-clipping: heap used ➡️ NaN MB
dom-widget-clipping: DOM nodes ▇▇▄▇▅▄█▇▅▁▅▄▇▃▄ ➡️ 21
dom-widget-clipping: event listeners ▅▅▅▅▁▅██▁▁▁▁█▁▁ 📉 2
large-graph-idle: avg frame time ▅▅▅▅▅▂▁▂▄▅▄▂▂▅█ ➡️ 17ms
large-graph-idle: p95 frame time ➡️ NaNms
large-graph-idle: layout duration ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0ms
large-graph-idle: style recalc duration ▅▅▅▆▄▅▃▄▅▅▆█▁▄▆ ➡️ 13ms
large-graph-idle: layout count ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0
large-graph-idle: style recalc count █▆█▃▃▁▃▆▃▆▆▃▆██ ➡️ 12
large-graph-idle: task duration ▂▃▂▆▂▃▃▇▅▃██▁▂▅ ➡️ 569ms
large-graph-idle: script duration ▄▅▄▆▄▅▅▇▆▅█▆▁▃▆ ➡️ 110ms
large-graph-idle: TBT ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0ms
large-graph-idle: heap used ➡️ NaN MB
large-graph-idle: DOM nodes ▆█▅▂▅▃▁▂▃▅▅▆▂▆▅ ➡️ 25
large-graph-idle: event listeners ███▇██▄▁▄▇▇█▂█▇ ➡️ 29
large-graph-pan: avg frame time ▆▃▃▆█▃▁█▆▆▆▆█▁▆ ➡️ 17ms
large-graph-pan: p95 frame time ➡️ NaNms
large-graph-pan: layout duration ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0ms
large-graph-pan: style recalc duration ▃▂▄▄▁▅▂▂▁▄▄█▃▁▂ ➡️ 17ms
large-graph-pan: layout count ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0
large-graph-pan: style recalc count ▆▃█▂▃▂▂▂▁▇▅▃█▆▃ ➡️ 69
large-graph-pan: task duration ▄▃▄▆▄▄▄▆▄▄█▆▁▂▅ ➡️ 1100ms
large-graph-pan: script duration ▅▄▅▆▆▅▄▆▄▅█▄▁▄▅ ➡️ 413ms
large-graph-pan: TBT ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0ms
large-graph-pan: heap used ➡️ NaN MB
large-graph-pan: DOM nodes ▅▃▆▂▄▁▃▁▁▅▁▂█▅▂ ➡️ 18
large-graph-pan: event listeners █▆█▁▁▆▁▁▃▆▁▃██▃ ➡️ 5
minimap-idle: avg frame time ▃▆▆▃█▁█▆▆▃▃▆█▆█ ➡️ 17ms
minimap-idle: p95 frame time ➡️ NaNms
minimap-idle: layout duration ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0ms
minimap-idle: style recalc duration ▄█▁█▅▅█▅▅▃▅▁▁▄▆ ➡️ 10ms
minimap-idle: layout count ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0
minimap-idle: style recalc count ▃▅▂▄█▃▆▁▂▅▂▁▅▆▃ ➡️ 9
minimap-idle: task duration ▃▄▁▅▁▃▄▅▇▃█▅▁▁▅ ➡️ 547ms
minimap-idle: script duration ▄▆▃▇▃▅▆▆▇▅█▅▁▃▆ ➡️ 106ms
minimap-idle: TBT ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0ms
minimap-idle: heap used ➡️ NaN MB
minimap-idle: DOM nodes ▃▅▂▄█▃▆▁▂▅▂▁▅▆▃ ➡️ 19
minimap-idle: event listeners ▃▃▆▁▁▁▃▁▁▆▁▃█▆▁ ➡️ 4
subgraph-dom-widget-clipping: avg frame time ▅▄▄▄▄▄█▄▄▄▃▁▆▃▃ ➡️ 17ms
subgraph-dom-widget-clipping: p95 frame time ➡️ NaNms
subgraph-dom-widget-clipping: layout duration ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0ms
subgraph-dom-widget-clipping: style recalc duration ▂▄▃▅▅▃▂▅▇▃▄█▁▄▆ ➡️ 14ms
subgraph-dom-widget-clipping: layout count ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0
subgraph-dom-widget-clipping: style recalc count ▇█▆▃▆▃▁▆█▇▃▆▇█▅ ➡️ 48
subgraph-dom-widget-clipping: task duration ▂▃▃▆▅▅▂▅█▂▆█▁▂▇ ➡️ 398ms
subgraph-dom-widget-clipping: script duration ▃▃▃▄▅▅▂▄█▂▅▇▁▂▅ ➡️ 131ms
subgraph-dom-widget-clipping: TBT ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0ms
subgraph-dom-widget-clipping: heap used ➡️ NaN MB
subgraph-dom-widget-clipping: DOM nodes ▅▇▅▂▅▂▁▅▅▅▁▇▅█▄ ➡️ 22
subgraph-dom-widget-clipping: event listeners ▅▅▅▂▅▁▅██▁▁█▅█▅ 📈 16
subgraph-idle: avg frame time ▆▆█▁▆▃▆▆▆▃▆▁▃▆█ ➡️ 17ms
subgraph-idle: p95 frame time ➡️ NaNms
subgraph-idle: layout duration ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0ms
subgraph-idle: style recalc duration ▁▇▃▆▂▄▂▃▃▆▆▄▃▇█ ➡️ 12ms
subgraph-idle: layout count ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0
subgraph-idle: style recalc count ▃▆▃▃▂▅▁▂▁▆▃▃██▇ ➡️ 12
subgraph-idle: task duration ▁▃▁▇▁▁▃▆▅▂█▅▁▁▄ ➡️ 378ms
subgraph-idle: script duration ▁▃▂▇▁▂▃▇▆▂█▅▂▁▅ ➡️ 22ms
subgraph-idle: TBT ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0ms
subgraph-idle: heap used ➡️ NaN MB
subgraph-idle: DOM nodes ▃▅▃▂▁▄▁▂▁▅▃▂▇█▇ ➡️ 24
subgraph-idle: event listeners ▁▅▁▁▁▁▁▁▁▅▄▁███ 📈 21
subgraph-mouse-sweep: avg frame time ▅▄▁▃▃▄▆▄▆▃▃█▁▃▃ ➡️ 17ms
subgraph-mouse-sweep: p95 frame time ➡️ NaNms
subgraph-mouse-sweep: layout duration ▁▄▄▄▃▃▅▅▅▂█▇▂▃▆ ➡️ 5ms
subgraph-mouse-sweep: style recalc duration ▃▂▄▅▂▃▄▅█▃█▆▁▂▅ ➡️ 43ms
subgraph-mouse-sweep: layout count ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 16
subgraph-mouse-sweep: style recalc count ▅▂▅▅▁▄▃▅█▅▆▄▂▄▅ ➡️ 81
subgraph-mouse-sweep: task duration ▃▂▄▅▂▄▄▅▇▄█▆▁▃▅ ➡️ 785ms
subgraph-mouse-sweep: script duration ▄▅▄▇▅▅▆▇▆▅██▁▄▆ ➡️ 105ms
subgraph-mouse-sweep: TBT ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0ms
subgraph-mouse-sweep: heap used ➡️ NaN MB
subgraph-mouse-sweep: DOM nodes ▅▁▄▅▁▄▃▃█▅▅▄▂▅▃ ➡️ 66
subgraph-mouse-sweep: event listeners ▇▁▂▇▁▂▂▂█▇▂▂▇▇▂ 📈 5
workflow-execution: avg frame time ▆▆▆▄▆▆▃▄▁▄█▆▅▄▆ ➡️ 17ms
workflow-execution: p95 frame time ➡️ NaNms
workflow-execution: layout duration ▁▆▁▃▂▄▃▂▃▃▅█▄▂▅ ➡️ 2ms
workflow-execution: style recalc duration ▃▇▅▇▁▅▆▇█▁██▂▄▆ ➡️ 25ms
workflow-execution: layout count ▁█▂▃▂▃▃▁▃▃▄▃▂▃▂ ➡️ 5
workflow-execution: style recalc count ▃█▅▇▁▄▅▆▅▅▅▅▄▄▂ ➡️ 15
workflow-execution: task duration ▂▅▄▅▁▄▆▆▆▁▇█▁▃▃ ➡️ 120ms
workflow-execution: script duration ▄▃▄▄▃▅▄▅▆▂▇█▁▃▄ ➡️ 29ms
workflow-execution: TBT ▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ➡️ 0ms
workflow-execution: heap used ➡️ NaN MB
workflow-execution: DOM nodes ▂█▃▆▁▄▃▅▃█▃▃▄▃▁ ➡️ 152
workflow-execution: event listeners ▅███▁▅███▁██▅█▅ ➡️ 49
Raw data
{
  "timestamp": "2026-07-29T04:13:27.794Z",
  "gitSha": "df909b27149a6c6fa75ca525fd3f3a565856f1b1",
  "branch": "wei/be-4782-reactivation-disclosure",
  "measurements": [
    {
      "name": "canvas-idle",
      "durationMs": 2126.67799999997,
      "styleRecalcs": 9,
      "styleRecalcDurationMs": 8.016,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 588.635,
      "heapDeltaBytes": 3832424,
      "heapUsedBytes": 71896500,
      "domNodes": 18,
      "jsHeapTotalBytes": 20574208,
      "scriptDurationMs": 18.918,
      "eventListeners": 4,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.699999999999818
    },
    {
      "name": "canvas-idle",
      "durationMs": 2018.1239999999434,
      "styleRecalcs": 8,
      "styleRecalcDurationMs": 7.908,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 446.426,
      "heapDeltaBytes": 3486588,
      "heapUsedBytes": 71349912,
      "domNodes": 16,
      "jsHeapTotalBytes": 20312064,
      "scriptDurationMs": 16.194,
      "eventListeners": 4,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.700000000000728
    },
    {
      "name": "canvas-mouse-sweep",
      "durationMs": 1870.6809999999905,
      "styleRecalcs": 75,
      "styleRecalcDurationMs": 35.097,
      "layouts": 12,
      "layoutDurationMs": 3.245,
      "taskDurationMs": 867.1610000000001,
      "heapDeltaBytes": -16794556,
      "heapUsedBytes": 50930844,
      "domNodes": -274,
      "jsHeapTotalBytes": 22011904,
      "scriptDurationMs": 124.611,
      "eventListeners": -148,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.66333333333332,
      "p95FrameDurationMs": 16.700000000000728
    },
    {
      "name": "canvas-mouse-sweep",
      "durationMs": 1823.240999999939,
      "styleRecalcs": 75,
      "styleRecalcDurationMs": 40.732,
      "layouts": 12,
      "layoutDurationMs": 3.699,
      "taskDurationMs": 827.3050000000001,
      "heapDeltaBytes": -1359032,
      "heapUsedBytes": 66530000,
      "domNodes": 57,
      "jsHeapTotalBytes": 21360640,
      "scriptDurationMs": 124.341,
      "eventListeners": 4,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.699999999999818
    },
    {
      "name": "canvas-zoom-sweep",
      "durationMs": 1721.4720000000625,
      "styleRecalcs": 31,
      "styleRecalcDurationMs": 16.156000000000002,
      "layouts": 6,
      "layoutDurationMs": 0.502,
      "taskDurationMs": 355.58299999999997,
      "heapDeltaBytes": 7080900,
      "heapUsedBytes": 74873224,
      "domNodes": 78,
      "jsHeapTotalBytes": 20574208,
      "scriptDurationMs": 18.229,
      "eventListeners": 19,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.700000000000728
    },
    {
      "name": "canvas-zoom-sweep",
      "durationMs": 1739.121999999952,
      "styleRecalcs": 32,
      "styleRecalcDurationMs": 16.244,
      "layouts": 6,
      "layoutDurationMs": 0.513,
      "taskDurationMs": 357.20399999999995,
      "heapDeltaBytes": 7186304,
      "heapUsedBytes": 75217296,
      "domNodes": 77,
      "jsHeapTotalBytes": 20574208,
      "scriptDurationMs": 17.929,
      "eventListeners": 19,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.700000000000728
    },
    {
      "name": "dom-widget-clipping",
      "durationMs": 589.2410000000154,
      "styleRecalcs": 11,
      "styleRecalcDurationMs": 7.209999999999999,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 365.815,
      "heapDeltaBytes": -12362404,
      "heapUsedBytes": 55553184,
      "domNodes": 18,
      "jsHeapTotalBytes": 21884928,
      "scriptDurationMs": 58.788999999999994,
      "eventListeners": 2,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.800000000000182
    },
    {
      "name": "dom-widget-clipping",
      "durationMs": 562.5240000000531,
      "styleRecalcs": 11,
      "styleRecalcDurationMs": 7.734,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 367.004,
      "heapDeltaBytes": -12098952,
      "heapUsedBytes": 55846108,
      "domNodes": 18,
      "jsHeapTotalBytes": 21360640,
      "scriptDurationMs": 57.352,
      "eventListeners": 2,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.800000000000182
    },
    {
      "name": "large-graph-idle",
      "durationMs": 2061.0529999999017,
      "styleRecalcs": 9,
      "styleRecalcDurationMs": 8.116000000000001,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 665.9170000000001,
      "heapDeltaBytes": 6868904,
      "heapUsedBytes": 65572448,
      "domNodes": -274,
      "jsHeapTotalBytes": 4517888,
      "scriptDurationMs": 108.177,
      "eventListeners": -144,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.800000000000182
    },
    {
      "name": "large-graph-idle",
      "durationMs": 2022.7919999999813,
      "styleRecalcs": 8,
      "styleRecalcDurationMs": 7.546,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 658.336,
      "heapDeltaBytes": 6003316,
      "heapUsedBytes": 64733844,
      "domNodes": -278,
      "jsHeapTotalBytes": 4517888,
      "scriptDurationMs": 103.53999999999999,
      "eventListeners": -144,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.800000000000182
    },
    {
      "name": "large-graph-pan",
      "durationMs": 2152.421000000004,
      "styleRecalcs": 68,
      "styleRecalcDurationMs": 12.658,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 1260.565,
      "heapDeltaBytes": 4642300,
      "heapUsedBytes": 64667944,
      "domNodes": -278,
      "jsHeapTotalBytes": 4460544,
      "scriptDurationMs": 427.962,
      "eventListeners": -142,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.700000000000728
    },
    {
      "name": "large-graph-pan",
      "durationMs": 2223.9900000000716,
      "styleRecalcs": 69,
      "styleRecalcDurationMs": 14.904,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 1339.0249999999999,
      "heapDeltaBytes": 5404564,
      "heapUsedBytes": 65208720,
      "domNodes": -276,
      "jsHeapTotalBytes": 4460544,
      "scriptDurationMs": 461.094,
      "eventListeners": -144,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.800000000000182
    },
    {
      "name": "large-graph-zoom",
      "durationMs": 3198.682000000076,
      "styleRecalcs": 66,
      "styleRecalcDurationMs": 14.478000000000002,
      "layouts": 60,
      "layoutDurationMs": 6.942,
      "taskDurationMs": 1475.6609999999998,
      "heapDeltaBytes": -1148100,
      "heapUsedBytes": 59994168,
      "domNodes": -279,
      "jsHeapTotalBytes": 7401472,
      "scriptDurationMs": 522.2509999999999,
      "eventListeners": -148,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.800000000000182
    },
    {
      "name": "large-graph-zoom",
      "durationMs": 3179.8290000000407,
      "styleRecalcs": 66,
      "styleRecalcDurationMs": 15.075999999999999,
      "layouts": 60,
      "layoutDurationMs": 7.067,
      "taskDurationMs": 1501.309,
      "heapDeltaBytes": -3605092,
      "heapUsedBytes": 57955768,
      "domNodes": -281,
      "jsHeapTotalBytes": 7401472,
      "scriptDurationMs": 519.856,
      "eventListeners": -148,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.700000000000728
    },
    {
      "name": "minimap-idle",
      "durationMs": 2041.5249999999787,
      "styleRecalcs": 8,
      "styleRecalcDurationMs": 6.758,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 649.372,
      "heapDeltaBytes": 7461628,
      "heapUsedBytes": 67738468,
      "domNodes": -277,
      "jsHeapTotalBytes": 4517888,
      "scriptDurationMs": 102.348,
      "eventListeners": -144,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.700000000000728
    },
    {
      "name": "minimap-idle",
      "durationMs": 2054.322999999954,
      "styleRecalcs": 9,
      "styleRecalcDurationMs": 7.922999999999999,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 670.5429999999999,
      "heapDeltaBytes": 7193108,
      "heapUsedBytes": 67577824,
      "domNodes": -276,
      "jsHeapTotalBytes": 4780032,
      "scriptDurationMs": 108.69500000000001,
      "eventListeners": -142,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.699999999999818
    },
    {
      "name": "subgraph-dom-widget-clipping",
      "durationMs": 600.3069999999298,
      "styleRecalcs": 47,
      "styleRecalcDurationMs": 9.604999999999999,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 388.73400000000004,
      "heapDeltaBytes": -11777656,
      "heapUsedBytes": 56205836,
      "domNodes": 20,
      "jsHeapTotalBytes": 22147072,
      "scriptDurationMs": 115.426,
      "eventListeners": 8,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.800000000000182
    },
    {
      "name": "subgraph-dom-widget-clipping",
      "durationMs": 578.8149999999632,
      "styleRecalcs": 48,
      "styleRecalcDurationMs": 11.607999999999999,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 389.496,
      "heapDeltaBytes": -11937132,
      "heapUsedBytes": 56162052,
      "domNodes": 22,
      "jsHeapTotalBytes": 22147072,
      "scriptDurationMs": 116.022,
      "eventListeners": 8,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.799999999999272
    },
    {
      "name": "subgraph-idle",
      "durationMs": 1993.4379999999692,
      "styleRecalcs": 11,
      "styleRecalcDurationMs": 9.032,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 437.602,
      "heapDeltaBytes": 3462752,
      "heapUsedBytes": 71486216,
      "domNodes": 22,
      "jsHeapTotalBytes": 21098496,
      "scriptDurationMs": 12.473999999999998,
      "eventListeners": 4,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.799999999999272
    },
    {
      "name": "subgraph-idle",
      "durationMs": 1993.4919999999465,
      "styleRecalcs": 10,
      "styleRecalcDurationMs": 8.977,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 440.546,
      "heapDeltaBytes": 3411852,
      "heapUsedBytes": 71195596,
      "domNodes": 20,
      "jsHeapTotalBytes": 21360640,
      "scriptDurationMs": 13.699000000000002,
      "eventListeners": 4,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.800000000000182
    },
    {
      "name": "subgraph-mouse-sweep",
      "durationMs": 1718.8809999998966,
      "styleRecalcs": 76,
      "styleRecalcDurationMs": 35.686,
      "layouts": 16,
      "layoutDurationMs": 4.308999999999999,
      "taskDurationMs": 785.44,
      "heapDeltaBytes": -19070024,
      "heapUsedBytes": 49026984,
      "domNodes": -274,
      "jsHeapTotalBytes": 20701184,
      "scriptDurationMs": 89.473,
      "eventListeners": -148,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.700000000000728
    },
    {
      "name": "subgraph-mouse-sweep",
      "durationMs": 1715.1270000000522,
      "styleRecalcs": 78,
      "styleRecalcDurationMs": 35.74,
      "layouts": 16,
      "layoutDurationMs": 3.6430000000000002,
      "taskDurationMs": 779.0030000000002,
      "heapDeltaBytes": -20686828,
      "heapUsedBytes": 47374904,
      "domNodes": -271,
      "jsHeapTotalBytes": 20963328,
      "scriptDurationMs": 88.369,
      "eventListeners": -148,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.700000000000728
    },
    {
      "name": "subgraph-transition-enter",
      "durationMs": 1400.9060000000773,
      "styleRecalcs": 19,
      "styleRecalcDurationMs": 30.938000000000006,
      "layouts": 15,
      "layoutDurationMs": 12.850999999999997,
      "taskDurationMs": 977.611,
      "heapDeltaBytes": 31354760,
      "heapUsedBytes": 97503676,
      "domNodes": 13673,
      "jsHeapTotalBytes": 14680064,
      "scriptDurationMs": 35.84899999999999,
      "eventListeners": 2371,
      "totalBlockingTimeMs": 135,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.799999999999272
    },
    {
      "name": "viewport-pan-sweep",
      "durationMs": 8218.391999999994,
      "styleRecalcs": 249,
      "styleRecalcDurationMs": 34.291999999999994,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 4323.392,
      "heapDeltaBytes": 9659992,
      "heapUsedBytes": 69418680,
      "domNodes": -274,
      "jsHeapTotalBytes": 6295552,
      "scriptDurationMs": 1351.1029999999998,
      "eventListeners": -128,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.700000000000728
    },
    {
      "name": "viewport-pan-sweep",
      "durationMs": 8254.14699999999,
      "styleRecalcs": 249,
      "styleRecalcDurationMs": 34.533,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 4373.611,
      "heapDeltaBytes": 1789336,
      "heapUsedBytes": 60747728,
      "domNodes": -277,
      "jsHeapTotalBytes": 5566464,
      "scriptDurationMs": 1350.093,
      "eventListeners": -128,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.700000000000728
    },
    {
      "name": "vue-large-graph-idle",
      "durationMs": 16255.204000000049,
      "styleRecalcs": 0,
      "styleRecalcDurationMs": 0,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 16234.943000000001,
      "heapDeltaBytes": -42778656,
      "heapUsedBytes": 165792572,
      "domNodes": -8312,
      "jsHeapTotalBytes": -16977920,
      "scriptDurationMs": 531.005,
      "eventListeners": -16387,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 17.776666666666763,
      "p95FrameDurationMs": 16.799999999999272
    },
    {
      "name": "vue-large-graph-idle",
      "durationMs": 16595.123000000058,
      "styleRecalcs": 0,
      "styleRecalcDurationMs": 0,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 16557.524,
      "heapDeltaBytes": -42028540,
      "heapUsedBytes": 166934252,
      "domNodes": -8312,
      "jsHeapTotalBytes": -13570048,
      "scriptDurationMs": 544.038,
      "eventListeners": -16387,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 17.219999999999953,
      "p95FrameDurationMs": 16.799999999999272
    },
    {
      "name": "vue-large-graph-pan",
      "durationMs": 20034.28500000007,
      "styleRecalcs": 134,
      "styleRecalcDurationMs": 14.565999999999967,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 20009.963,
      "heapDeltaBytes": -60401204,
      "heapUsedBytes": 166463224,
      "domNodes": -8312,
      "jsHeapTotalBytes": -13570048,
      "scriptDurationMs": 889.438,
      "eventListeners": -16381,
      "totalBlockingTimeMs": 114,
      "frameDurationMs": 17.776666666666763,
      "p95FrameDurationMs": 16.799999999999272
    },
    {
      "name": "vue-large-graph-pan",
      "durationMs": 19908.199999999964,
      "styleRecalcs": 137,
      "styleRecalcDurationMs": 14.259000000000022,
      "layouts": 0,
      "layoutDurationMs": 0,
      "taskDurationMs": 19880.882,
      "heapDeltaBytes": -42204616,
      "heapUsedBytes": 166319496,
      "domNodes": -8312,
      "jsHeapTotalBytes": -14618624,
      "scriptDurationMs": 819.7869999999999,
      "eventListeners": -16387,
      "totalBlockingTimeMs": 52,
      "frameDurationMs": 18.886666666666617,
      "p95FrameDurationMs": 16.799999999999272
    },
    {
      "name": "workflow-execution",
      "durationMs": 101.13799999999173,
      "styleRecalcs": 7,
      "styleRecalcDurationMs": 12.169,
      "layouts": 2,
      "layoutDurationMs": 0.406,
      "taskDurationMs": 71.975,
      "heapDeltaBytes": 3027480,
      "heapUsedBytes": 70132656,
      "domNodes": 110,
      "jsHeapTotalBytes": 3145728,
      "scriptDurationMs": 7.566000000000002,
      "eventListeners": 33,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.663333333333338,
      "p95FrameDurationMs": 16.700000000000273
    },
    {
      "name": "workflow-execution",
      "durationMs": 480.38500000006934,
      "styleRecalcs": 13,
      "styleRecalcDurationMs": 22.37,
      "layouts": 3,
      "layoutDurationMs": 0.6629999999999999,
      "taskDurationMs": 117.10300000000001,
      "heapDeltaBytes": -16130076,
      "heapUsedBytes": 51112772,
      "domNodes": 119,
      "jsHeapTotalBytes": 7729152,
      "scriptDurationMs": 9.901,
      "eventListeners": 65,
      "totalBlockingTimeMs": 0,
      "frameDurationMs": 16.666666666666668,
      "p95FrameDurationMs": 16.800000000000182
    }
  ]
}

@coderabbitai

coderabbitai Bot commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It 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 reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

The subscription transition and downgrade flows now display reactivation disclosures, collect required consent, prevent unconfirmed cancelled-subscription changes, and forward confirmation through checkout to /api/billing/subscribe.

Changes

Subscription reactivation confirmation

Layer / File(s) Summary
Reactivation preview and disclosure UI
src/locales/en/main.json, src/platform/workspace/components/SubscriptionTransitionPreviewWorkspace.vue, src/platform/workspace/components/SubscriptionTransitionPreviewWorkspace*.test.ts, src/platform/workspace/components/SubscriptionTransitionPreviewWorkspace.stories.ts
Cancelled-subscription transitions render localized details, threshold-based checkbox gating, dynamic CTA text, fallback payment dates, Storybook scenarios, and a boolean confirm payload.
Downgrade preview and dialog gating
src/platform/workspace/composables/useDowngradeToPersonal.ts, src/services/dialogService.ts, src/platform/workspace/components/dialogs/DowngradeRemoveMembersDialogContent.vue, src/**/*downgrade*.test.ts
Downgrade previews identify required reactivation confirmation and charges, block unconfirmed member-removal flows, and pass confirmation state through the dialog.
Checkout and billing consent propagation
src/platform/workspace/composables/useSubscriptionCheckout.ts, src/platform/workspace/api/workspaceApi.ts, src/**/*checkout*.test.ts, src/platform/workspace/api/workspaceApi.test.ts, src/platform/telemetry/types.ts
Checkout blocks cancelled-subscription requests without consent, forwards confirmReactivation, serializes confirm_reactivation, and adds related telemetry codes.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant SubscriptionTransitionPreviewWorkspace
  participant dialogService
  participant useDowngradeToPersonal
  participant useSubscriptionCheckout
  participant workspaceApi
  SubscriptionTransitionPreviewWorkspace->>useSubscriptionCheckout: emit confirmReactivation
  dialogService->>useDowngradeToPersonal: previewDowngrade
  useDowngradeToPersonal->>dialogService: return charge and confirmation requirement
  dialogService->>useDowngradeToPersonal: downgradeToPersonal(confirmReactivation)
  useSubscriptionCheckout->>workspaceApi: subscribe(confirmReactivation)
  workspaceApi->>workspaceApi: POST /api/billing/subscribe with confirm_reactivation
Loading

Possibly related PRs

Suggested labels: released:cloud, size:L, cloud/1.45, cloud/1.47

Suggested reviewers: austinmroz


Important

Pre-merge checks failed

Please resolve all errors before merging. Addressing warnings is optional.

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
End-To-End Regression Coverage For Fixes ❓ Inconclusive Commit subjects and PR title are not available here, so the bug-fix signal required to apply this check can’t be verified. Please provide the PR title and commit subjects (or a complete changed-file/commit list) so I can determine whether E2E coverage was required.
✅ Passed checks (5 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Adr Compliance For Entity/Litegraph Changes ✅ Passed No changed files touch src/lib/litegraph/, src/ecs/, or graph-entity paths, so ADR 0003/0008 checks are not applicable.
Title check ✅ Passed The title clearly summarizes the main billing change: plan changes on cancelled subscriptions now require/reactivate disclosure.
Description check ✅ Passed The description is detailed and covers summary, rationale, scope, and verification, even though it doesn't use the exact template headings.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch wei/be-4782-reactivation-disclosure

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.

@wei-hai wei-hai added the cursor-review Trigger multi-model Cursor agent review on this PR label Jul 28, 2026

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@src/platform/workspace/components/SubscriptionTransitionPreviewWorkspace.vue`:
- Around line 316-322: Update currentMonthlyPriceCents to reuse the existing
currentIsYearly computed value when deciding whether to divide plan.price_cents
by 12, removing the duplicate plan.duration === 'ANNUAL' check while preserving
the existing monthly-price calculation.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: f43bcf9d-0b63-46a0-bcd5-5620b19ca398

📥 Commits

Reviewing files that changed from the base of the PR and between ceb1189 and 1331c71.

📒 Files selected for processing (8)
  • src/locales/en/main.json
  • src/platform/workspace/api/workspaceApi.test.ts
  • src/platform/workspace/api/workspaceApi.ts
  • src/platform/workspace/components/SubscriptionTransitionPreviewWorkspace.test.ts
  • src/platform/workspace/components/SubscriptionTransitionPreviewWorkspace.vue
  • src/platform/workspace/components/SubscriptionTransitionPreviewWorkspaceReactivation.test.ts
  • src/platform/workspace/composables/useSubscriptionCheckout.test.ts
  • src/platform/workspace/composables/useSubscriptionCheckout.ts

Comment thread src/platform/workspace/components/SubscriptionTransitionPreviewWorkspace.vue Outdated
@codecov

codecov Bot commented Jul 28, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 97.57282% with 5 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
...ponents/SubscriptionTransitionPreviewWorkspace.vue 95.69% 4 Missing ⚠️
...m/workspace/composables/useSubscriptionCheckout.ts 98.24% 1 Missing ⚠️
@@            Coverage Diff             @@
##             main   #14222      +/-   ##
==========================================
- Coverage   79.29%   78.90%   -0.39%     
==========================================
  Files        1751     1755       +4     
  Lines       99965   101420    +1455     
  Branches    32413    33569    +1156     
==========================================
+ Hits        79271    80030     +759     
- Misses      20298    20960     +662     
- Partials      396      430      +34     
Flag Coverage Δ
unit 69.47% <97.57%> (+0.39%) ⬆️

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

Files with missing lines Coverage Δ
src/platform/telemetry/types.ts 100.00% <ø> (ø)
src/platform/workspace/api/workspaceApi.ts 55.37% <ø> (+0.21%) ⬆️
...ts/dialogs/DowngradeRemoveMembersDialogContent.vue 100.00% <100.00%> (ø)
...rm/workspace/composables/useDowngradeToPersonal.ts 99.03% <100.00%> (+0.30%) ⬆️
src/services/dialogService.ts 62.71% <100.00%> (+2.71%) ⬆️
...m/workspace/composables/useSubscriptionCheckout.ts 97.44% <98.24%> (+1.17%) ⬆️
...ponents/SubscriptionTransitionPreviewWorkspace.vue 97.58% <95.69%> (+<0.01%) ⬆️

... and 39 files with indirect coverage changes

🚀 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.

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔍 Cursor Review — Consolidated panel

Triggered by @wei-hai.

Found 10 finding(s).

Severity Count
🟠 High 4
🟡 Medium 2
🟢 Low 4

Panel: 8/8 reviewers contributed findings.

Comment thread src/platform/workspace/composables/useSubscriptionCheckout.ts
Comment thread src/platform/workspace/components/SubscriptionTransitionPreviewWorkspace.vue Outdated
Comment thread src/platform/workspace/composables/useSubscriptionCheckout.ts
Comment thread src/platform/workspace/components/SubscriptionTransitionPreviewWorkspace.vue Outdated
Comment thread src/platform/workspace/components/SubscriptionTransitionPreviewWorkspace.vue Outdated
Comment thread src/platform/workspace/components/SubscriptionTransitionPreviewWorkspace.vue Outdated
The comment named an internal tracker issue, which does not belong in a
public repo. The guard it describes is unchanged.
@wei-hai wei-hai changed the title feat(billing): disclose reactivation when changing plans from a cancelled subscription (BE-4782) feat(billing): disclose reactivation when changing plans from a cancelled subscription Jul 28, 2026
…ade/checkout paths

Critical: downgradeToPersonal() removed team members before subscribing, and
that subscribe never sent confirm_reactivation. For a cancelled team
subscription, this meant members were deleted and the plan change was then
rejected by the API. downgradeToPersonal() now takes a confirmReactivation
flag, checks whether the transition requires it before touching membership,
and throws (removing nobody) if consent wasn't given. showDowngradeToPersonalDialog
now previews up front and shows a real disclosure (with the exact charge) for
either member removal or reactivation, so the flag comes from actual user
consent, not from subscription state.

Also fixes 9 review findings on the reactivation disclosure banner and
checkout paths:
- Show the exact charge in cents instead of rounding to whole dollars.
- Give annual->monthly duration changes their own copy instead of reusing
  the annual-only "full year billed" text.
- Block (instead of silently sending confirmReactivation:false) on checkout
  paths that have no banner to collect consent.
- Disable confirm until billing context has loaded, so a fast preview can't
  race a not-yet-loaded cancellation state.
- Reset a ticked consent checkbox when the charge amount changes.
- Use seat_summary.total_cost_cents (whole-subscription) instead of the
  per-seat price_cents for the charge threshold.
- Never show the checkbox on the downgrade variant, whose copy already says
  no charge applies today.
- Fall back to one billing period after activation, not the activation date
  itself, when period_end is missing.
- Hide the banner when required data (endDate, current_plan) is missing
  instead of rendering broken copy.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@wei-hai

wei-hai commented Jul 28, 2026

Copy link
Copy Markdown
Contributor Author

All 10 review findings addressed. None were rejected — every one reproduced.

The critical one first. useDowngradeToPersonal removed team members before the API's consent requirement was ever checked, so a cancelled team subscription downgrading to personal would have deleted members and then been rejected — members gone, subscription unchanged, nothing to undo. The fix checks consent immediately after the preview succeeds and before touching membership, throwing so the caller can collect consent and retry rather than losing members on a request that was always going to fail.

The remove-then-subscribe ordering is genuinely forced, confirmed two ways: the composable's own docstring already flags the non-atomic orchestration as known temporary behaviour pending a backend endpoint, and the personal plan carries max_seats: 1 with an exceeds_max_seats availability reason — a workspace with other members cannot fit it, so the subscribe would be rejected while they remain. So the ordering stays; the bug was removing unconditionally.

To supply a real (non-derived) consent value, the dialog now previews up front and always shows a confirmation when either members would be removed or a reactivation needs disclosing, instead of skipping straight to a doomed request. Its existing type-to-confirm gate is the source of the flag.

The other three Highs: the acknowledged amount no longer rounds to whole dollars (.54 was displaying as $55 on the one control that makes consent meaningful); duration_change now branches on the target duration, so an annual→monthly switch no longer promises a full-year charge; and the team/add-payment paths now block with a toast rather than silently sending a consent value the server was always going to reject.

Mediums: confirm is disabled until billing state has loaded, closing the race where a preview resolving first let someone confirm with no disclosure; and consent now resets when the charge changes, so a box ticked for a small amount can't carry onto a larger one.

Lows: threshold now uses the whole-subscription total rather than a per-seat price, the checkbox is excluded from the downgrade variant whose copy promises no charge, the renewal date no longer falls back to the activation date, and isReactivating guards its inputs.

Each fix is mutation-verified — the pre-fix file restored, tests confirmed failing, then restored. I re-ran the critical one independently: neutering the consent guard fails exactly the one test asserting no member is removed, and nothing else.

Also stripped two pre-existing internal tracker IDs from doc comments on functions being edited, since this repo is public.

Known and unchanged: if the subscribe fails for an unrelated reason after members are removed, they stay removed. That is pre-existing — the codebase already has a dedicated error string for it — and belongs with the atomic-endpoint work, not here.

Nothing outside the composable catches it by type — the caller avoids the
case up front and the generic handler surfaces its message — so exporting
it only widened the module's surface.

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/platform/workspace/components/SubscriptionTransitionPreviewWorkspace.vue (1)

453-468: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

confirmCta isn't gated by isReactivating, unlike every other reactivation-derived computed.

reactivationVariant only requires isCancelled + transition_type — it doesn't require subscription.endDate or previewData.current_plan the way isReactivating does. exceedsMonthlyThreshold and confirmReactivation both correctly check isReactivating.value first, but confirmCta branches straight on reactivationVariant.value.

Concretely: if the subscription is cancelled but endDate hasn't loaded yet (or current_plan is briefly missing), the banner is correctly hidden (isReactivating false) and confirmReactivation correctly emits false — but the button still renders "Confirm & reactivate — {amount} today" (or "Confirm & reactivate" for downgrade), falsely implying a reactivation charge that was never disclosed and won't actually be confirmed. Neither of the "missing reactivation data" tests catch this since they only assert the banner/checkbox are absent, not the button label.

🐛 Proposed fix
 const confirmCta = computed(() => {
+  if (!isReactivating.value) {
+    return isImmediate.value
+      ? t('subscription.preview.confirmUpgradeCta')
+      : t('subscription.preview.confirmChange')
+  }
   if (reactivationVariant.value === 'downgrade') {
     return t('subscription.preview.reactivation.confirmButton')
   }
-  if (
-    reactivationVariant.value === 'upgrade' ||
-    reactivationVariant.value === 'duration_change'
-  ) {
-    return t('subscription.preview.reactivation.confirmButtonWithCharge', {
-      amount: chargeDisplay.value
-    })
-  }
-  return isImmediate.value
-    ? t('subscription.preview.confirmUpgradeCta')
-    : t('subscription.preview.confirmChange')
+  return t('subscription.preview.reactivation.confirmButtonWithCharge', {
+    amount: chargeDisplay.value
+  })
 })
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/platform/workspace/components/SubscriptionTransitionPreviewWorkspace.vue`
around lines 453 - 468, Gate the reactivation-specific branches in confirmCta by
isReactivating.value before evaluating reactivationVariant.value. When
reactivation data is incomplete and isReactivating is false, fall through to the
existing non-reactivation CTA logic; preserve the current downgrade and
upgrade/duration-change labels only for active reactivation flows.
🤖 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/components/dialogs/DowngradeRemoveMembersDialogContent.test.ts`:
- Around line 104-112: Update the test named “shows the removal-only body when
reactivation is not required” to mount the component with requiresRemoval set to
true and requiresReactivation set to false. Keep the existing assertion for the
immediate member-removal text, ensuring the test exercises the intended
removal-only branch rather than the default fallback.

In `@src/platform/workspace/composables/useDowngradeToPersonal.ts`:
- Around line 82-89: Update requiresReactivationConfirmation and its
downgradeToPersonal call-site logic to fail closed when subscription.value is
unavailable: require reactivation confirmation when the subscription is missing
or cancelled, unless preview.transition_type is 'new_subscription'. Remove the
nullish fallback that treats an unset subscription as active, while preserving
the existing behavior for available subscription data.

In `@src/services/dialogService.ts`:
- Around line 675-696: Prevent the downgrade flow from billing an amount
different from the one confirmed in showDowngradeToPersonalDialog: update the
interaction between previewDowngrade and downgradeToPersonal so the fetched
preview is reused, or ensure downgradeToPersonal validates its fresh preview
cost against the confirmed chargeCents and rejects or re-prompts on mismatch.
Keep the confirmation amount and actual billing amount consistent across the
onConfirm path.

---

Outside diff comments:
In
`@src/platform/workspace/components/SubscriptionTransitionPreviewWorkspace.vue`:
- Around line 453-468: Gate the reactivation-specific branches in confirmCta by
isReactivating.value before evaluating reactivationVariant.value. When
reactivation data is incomplete and isReactivating is false, fall through to the
existing non-reactivation CTA logic; preserve the current downgrade and
upgrade/duration-change labels only for active reactivation flows.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 678689cd-3e2f-4436-9645-438a186d82a9

📥 Commits

Reviewing files that changed from the base of the PR and between b3e4e65 and ed8543c.

📒 Files selected for processing (13)
  • src/locales/en/main.json
  • src/platform/telemetry/types.ts
  • src/platform/workspace/components/SubscriptionTransitionPreviewWorkspace.test.ts
  • src/platform/workspace/components/SubscriptionTransitionPreviewWorkspace.vue
  • src/platform/workspace/components/SubscriptionTransitionPreviewWorkspaceReactivation.test.ts
  • src/platform/workspace/components/dialogs/DowngradeRemoveMembersDialogContent.test.ts
  • src/platform/workspace/components/dialogs/DowngradeRemoveMembersDialogContent.vue
  • src/platform/workspace/composables/useDowngradeToPersonal.test.ts
  • src/platform/workspace/composables/useDowngradeToPersonal.ts
  • src/platform/workspace/composables/useSubscriptionCheckout.test.ts
  • src/platform/workspace/composables/useSubscriptionCheckout.ts
  • src/services/dialogService.downgrade.test.ts
  • src/services/dialogService.ts

Comment thread src/platform/workspace/composables/useDowngradeToPersonal.ts
Comment thread src/services/dialogService.ts
The downgrade dialog previewed once to decide what to show, then
downgradeToPersonal previewed again before billing, with nothing checking
the two agreed. Consent to a specific figure could silently apply to a
different one.

The charge is computed server-side inside subscribe, so passing the first
preview forward would not pin the amount — it would only stop the code
re-checking. Instead the confirmed figure is passed down and compared
against the fresh preview, refusing before any member is removed.

Also fail closed when billing context has not loaded: unknown
subscription state now requires confirmation rather than reading as
'not cancelled', matching the preview UI's existing gating.

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/platform/workspace/composables/useDowngradeToPersonal.ts`:
- Around line 92-99: The requiresReactivationConfirmation function must use an
authoritative reactivation-required field from PreviewSubscribeResponse instead
of isInitialized and cached subscription.value status. Add that field to the
preview contract, then return false only for new_subscription or an explicitly
false authoritative value; treat an unavailable value as requiring confirmation
to fail closed.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 7b0afc37-68f8-4345-b9d4-98981d0fd31e

📥 Commits

Reviewing files that changed from the base of the PR and between ed8543c and 801284e.

📒 Files selected for processing (7)
  • src/locales/en/main.json
  • src/platform/telemetry/types.ts
  • src/platform/workspace/components/dialogs/DowngradeRemoveMembersDialogContent.test.ts
  • src/platform/workspace/composables/useDowngradeToPersonal.test.ts
  • src/platform/workspace/composables/useDowngradeToPersonal.ts
  • src/services/dialogService.downgrade.test.ts
  • src/services/dialogService.ts

Comment thread src/platform/workspace/composables/useDowngradeToPersonal.ts Outdated
wei-hai added 2 commits July 28, 2026 15:38
…ransitionPreviewWorkspace

Six stories covering the reactivation banner: an ordinary (non-cancelled)
upgrade to prove the banner doesn't leak into normal flows, an immediate
upgrade with an exact-cents charge, a scheduled downgrade with $0 due today,
both cadence-change directions (monthly<->annual), and a charge above the
current plan's monthly total to render the consent checkbox with the confirm
button disabled until ticked.

Billing context is stubbed per story via the existing Storybook
setBillingContextMock helper, since the component reads
subscription/isInitialized from useBillingContext rather than props.
…tokens

The reactivation-disclosure banner used text-text-primary/text-text-secondary
and border-border-default, a minority token family in this codebase. The
nearest sibling (BillingStatusBanner.vue in the same settings dialogs
directory) instead pairs text-base-foreground/text-muted-foreground with
border-interface-stroke — align the banner's title, body, checkbox label, and
checkbox border to match, so both banners a user can see in the same flow
render the same greys. The warning-accent classes (border/bg/text on
warning-background) are unchanged; only the neutral tokens moved.
@dosubot dosubot Bot added size:XL This PR changes 500-999 lines, ignoring generated files. and removed size:L This PR changes 100-499 lines, ignoring generated files. labels Jul 28, 2026

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@src/platform/workspace/components/SubscriptionTransitionPreviewWorkspace.stories.ts`:
- Around line 67-79: Update cancelledSubscription to accept a duration
parameter, use that duration for SubscriptionInfo.duration, and derive planSlug
from the tier and supplied duration. Update each reactivation story, especially
ReactivatingAnnualToMonthly, to pass the duration matching its current_plan so
the mocked subscription contract remains consistent.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: df947b2d-16dc-4b10-948e-9b35ffe3d169

📥 Commits

Reviewing files that changed from the base of the PR and between 801284e and 644036e.

📒 Files selected for processing (2)
  • src/platform/workspace/components/SubscriptionTransitionPreviewWorkspace.stories.ts
  • src/platform/workspace/components/SubscriptionTransitionPreviewWorkspace.vue

Two review-thread fixes on the reactivation-disclosure flow:

- useDowngradeToPersonal: requiresReactivationConfirmation read the
  billing context's cached subscription snapshot, which can predate a
  cancellation that happened after the last load. Refresh via
  fetchStatus() right before evaluating the guard, both when deciding
  what to show (previewDowngrade) and again right before member
  removal (downgradeToPersonal), so a cancellation that occurred after
  the initial load isn't missed.

- SubscriptionTransitionPreviewWorkspace.stories.ts: cancelledSubscription()
  always hardcoded MONTHLY, so the ReactivatingAnnualToMonthly story's
  billing-context mock disagreed with its ANNUAL current_plan. The
  helper now takes a duration parameter and derives planSlug from it.

Verified with mutation testing: reverting the fetchStatus() calls
makes the two new regression tests fail as expected; restoring the
fix makes them pass again.
@wei-hai wei-hai changed the title feat(billing): disclose reactivation when changing plans from a cancelled subscription feat(billing): disclose that changing plans on a cancelled subscription resumes it Jul 28, 2026
@wei-hai

wei-hai commented Jul 28, 2026

Copy link
Copy Markdown
Contributor Author

Screenshots

Rendered from the six Storybook states added in this PR (SubscriptionTransitionPreviewWorkspace.stories.ts), so each is reproducible locally with pnpm storybook.

Baseline — subscription not cancelled. No banner, no checkbox, button unchanged. This is the regression guard: nothing about an ordinary plan change moves.

not-cancelled

Upgrade while cancelled. Banner names the end date, the exact charge, and the new renewal date. Charge exceeds the current monthly price, so the consent checkbox appears.

reactivating-upgrade

Above the threshold — the clearest view of the contract. The button reads "Confirm & reactivate — $80.00 today" and is disabled until the box is ticked. The amount in the button, the amount in the checkbox label, and Total due today are the same number by construction.

reactivating-above-threshold

Downgrade while cancelled — the case this feature exists for. $0 today, so no checkbox; the copy leads with reactivates instead of a number, because there is no number to catch the eye. Without this banner a user who deliberately cancelled clicks a cheaper plan and silently resumes renewing.

reactivating-downgrade

Monthly → annual. Full year billed today — the largest charge of the three, and the one where "just switching plans" is most misleading.

reactivating-monthly-to-annual

Annual → monthly. Distinct copy from the annual variant.

reactivating-annual-to-monthly

⚠️ This shot shows the pre-existing copy bug noted in the description: the credit line reads "Credit from current monthly plan" when the current plan is annual. creditFromPlanLabel returns the monthly string for any cadence change and never reads the current duration. It is on main, untouched here, and affects all cadence changes — flagged rather than fixed so this PR stays scoped. I confirmed it renders this way after correcting the story mock, so it is production copy, not the fixture.

Test cases

The disclosure itselfSubscriptionTransitionPreviewWorkspaceReactivation.test.ts (20)

Group Pins
Visibility hidden when not cancelled; hidden with no end date; hidden when the preview carries no current_plan
Per-variant copy upgrade / downgrade / monthly→annual / annual→monthly each distinct; downgrade mentions no charge
Threshold no checkbox at or below the current monthly price; checkbox required above it; multi-seat team plans compare the whole-subscription total, not the per-seat price
Consent payload confirmReactivation false when not cancelled; true below threshold; true above threshold only after ticking
Amount fidelity emits the exact fractional charge, never rounded to whole dollars
Resets a ticked box is cleared when the amount changes, so consent can't carry over to a different number
Init confirm stays disabled until billing context finishes loading, even below threshold

The consent isn't self-fulfillinguseDowngradeToPersonal.test.ts (36)

  • removes nobody and never subscribes when the subscription is cancelled and reactivation is not confirmed — the data-loss guard: the check runs before the member-removal loop, so a rejected downgrade can't destroy membership on the way out.
  • refuses to bill when the fresh preview cost no longer matches the confirmed charge — the amount consented to is the amount billed.
  • reports the cancellation discovered by refreshing status, not the stale cached read and refuses to remove members when a status refresh discovers a cancellation the cached read missed — added this round; the guard previously read a cached snapshot that could predate the cancellation.

WiringworkspaceApi.test.tssubscribe() sends confirm_reactivation when reactivating a cancelled subscription; useSubscriptionCheckout.test.tsforwards confirmReactivation true when the disclosure banner reports consent and blocks the team subscribe and shows an error for a cancelled subscription with no confirmation; dialogService.downgrade.test.tsforwards the confirmed reactivation flag from the dialog to downgradeToPersonal.

Mutation evidence

The two new guard tests were verified by reverting the fix, not just by watching them pass:

reverted both `await fetchStatus()` calls  → 2 failed, 34 passed
restored                                    → 36 passed

The two failures were exactly the two tests that claim to protect that behaviour. This matters here specifically: an earlier commit on this branch had consent derived from the very state it was attesting to, so it passed every test while being unable to fail.

coderabbitai[bot]
coderabbitai Bot previously approved these changes Jul 28, 2026

@dante01yoon dante01yoon left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Two blocking consent-path issues remain on the current head. Both can make the confirmation UI disagree with what is actually disclosed or charged.

Comment thread src/platform/workspace/composables/useSubscriptionCheckout.ts

@dante01yoon dante01yoon left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Re-reviewed the latest regression fixes. The five previously blocking scenarios are now covered:

  • amount drift updates the displayed preview and allows a newly confirmed retry;
  • downgrade amount/status drift clears the typed acknowledgement;
  • Team status refresh is inside loading/error/telemetry handling;
  • Team → personal uses subscription-status readiness rather than aggregate billing initialization;
  • unusable status-drift previews return the user to pricing instead of creating another blocked retry.

Focused validation passed locally: 107 checkout, downgrade, dialog, and reactivation component tests. I found no remaining blocking regression in the current head.

@wei-hai
wei-hai added this pull request to the merge queue Jul 29, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Jul 29, 2026
@wei-hai
wei-hai added this pull request to the merge queue Jul 29, 2026
Merged via the queue into main with commit 25f323e Jul 29, 2026
67 checks passed
@wei-hai
wei-hai deleted the wei/be-4782-reactivation-disclosure branch July 29, 2026 18:35
@dosubot

dosubot Bot commented Jul 29, 2026

Copy link
Copy Markdown

📄 Knowledge review

Dosu skipped reviewing this PR because your organization has used its 200 included credits for the month. Your usage will reset on 2026-08-01. To have Dosu review this PR before then, ask your organization admin to upgrade to a pro account.


Leave Feedback Ask Dosu about ComfyUI_frontend Add Dosu to your team

@comfy-pr-bot

Copy link
Copy Markdown
Member

@wei-hai Successfully backported to #14283

@comfy-pr-bot

Copy link
Copy Markdown
Member

@wei-hai Successfully backported to #14284

@comfy-pr-bot

Copy link
Copy Markdown
Member

@wei-hai Successfully backported to #14285

@github-actions github-actions Bot removed the needs-backport Fix/change that needs to be cherry-picked to the current feature freeze branch label Jul 29, 2026
comfy-pr-bot added a commit that referenced this pull request Jul 29, 2026
… cancelled subscription resumes it (#14284)

Backport of #14222 to `core/1.48`

Automatically created by backport workflow.

Co-authored-by: Wei Hai <hai.vincent@gmail.com>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
comfy-pr-bot added a commit that referenced this pull request Jul 29, 2026
…a cancelled subscription resumes it (#14285)

Backport of #14222 to `cloud/1.48`

Automatically created by backport workflow.

Co-authored-by: Wei Hai <hai.vincent@gmail.com>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
comfy-pr-bot added a commit that referenced this pull request Jul 29, 2026
…a cancelled subscription resumes it (#14283)

Backport of #14222 to `cloud/1.47`

Automatically created by backport workflow.

---------

Co-authored-by: Wei Hai <hai.vincent@gmail.com>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
@comfy-pr-bot comfy-pr-bot added the released:cloud PR has been released to cloud label Jul 31, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

cursor-review Trigger multi-model Cursor agent review on this PR preview released:cloud PR has been released to cloud size:XL This PR changes 500-999 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants