feat: attribute payment intent through paywall, checkout, and top-up telemetry - #13363
Conversation
🎨 Storybook: ✅ Built — View Storybook🎭 Playwright: ✅ 1693 passed, 0 failed · 1 flaky📊 Browser Reports
📦 Bundle: 7.77 MB gzip 🔴 +1.18 kBDetailsSummary
Category Glance App Entry Points — 47.4 kB (baseline 47.4 kB) • ⚪ 0 BMain entry bundles and manifests
Status: 1 added / 1 removed Graph Workspace — 1.25 MB (baseline 1.25 MB) • 🔴 +138 BGraph editor runtime, canvas, workflow orchestration
Status: 1 added / 1 removed Views & Navigation — 98 kB (baseline 97.7 kB) • 🔴 +348 BTop-level views, pages, and routed surfaces
Status: 9 added / 9 removed / 3 unchanged Panels & Settings — 546 kB (baseline 546 kB) • 🔴 +61 BConfiguration panels, inspectors, and settings screens
Status: 11 added / 11 removed / 16 unchanged User & Accounts — 26.9 kB (baseline 26.9 kB) • ⚪ 0 BAuthentication, profile, and account management bundles
Status: 7 added / 7 removed / 3 unchanged Editors & Dialogs — 117 kB (baseline 117 kB) • ⚪ 0 BModals, dialogs, drawers, and in-app editors
Status: 4 added / 4 removed / 1 unchanged UI Components — 57.2 kB (baseline 57.2 kB) • 🔴 +34 BReusable component library chunks
Status: 5 added / 5 removed / 8 unchanged Data & Services — 270 kB (baseline 270 kB) • ⚪ 0 BStores, services, APIs, and repositories
Status: 13 added / 13 removed / 3 unchanged Utilities & Hooks — 3.37 MB (baseline 3.37 MB) • 🔴 +1.49 kBHelpers, composables, and utility bundles
Status: 16 added / 16 removed / 17 unchanged Vendor & Third-Party — 15.3 MB (baseline 15.3 MB) • ⚪ 0 BExternal libraries and shared vendor chunks Status: 16 unchanged Other — 11.7 MB (baseline 11.7 MB) • 🔴 +2.62 kBBundles that do not match a named category
Status: 68 added / 68 removed / 97 unchanged ⚡ Performance Report
Show regressions
All metrics
Historical variance (last 15 runs)
Trend (last 15 commits on main)
Raw data{
"timestamp": "2026-07-02T00:19:45.155Z",
"gitSha": "91ca7731ec504d352a62ea37c7816bdfa7c96043",
"branch": "bl/dazzling-jackson-8d317b",
"measurements": [
{
"name": "canvas-idle",
"durationMs": 2049.6370000000184,
"styleRecalcs": 8,
"styleRecalcDurationMs": 7.276999999999998,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 450.68600000000004,
"heapDeltaBytes": 12741680,
"heapUsedBytes": 64410836,
"domNodes": -275,
"jsHeapTotalBytes": 15855616,
"scriptDurationMs": 20.872,
"eventListeners": -199,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "canvas-idle",
"durationMs": 2036.594999999977,
"styleRecalcs": 9,
"styleRecalcDurationMs": 7.786999999999999,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 420.7339999999999,
"heapDeltaBytes": -4477808,
"heapUsedBytes": 48400816,
"domNodes": -290,
"jsHeapTotalBytes": 16904192,
"scriptDurationMs": 19.629,
"eventListeners": -199,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "canvas-mouse-sweep",
"durationMs": 2046.4770000000385,
"styleRecalcs": 77,
"styleRecalcDurationMs": 47.699000000000005,
"layouts": 12,
"layoutDurationMs": 3.7369999999999997,
"taskDurationMs": 954.7620000000001,
"heapDeltaBytes": -5823240,
"heapUsedBytes": 46789964,
"domNodes": -277,
"jsHeapTotalBytes": 16642048,
"scriptDurationMs": 130.10000000000002,
"eventListeners": -199,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.699999999999818
},
{
"name": "canvas-mouse-sweep",
"durationMs": 1841.4960000000065,
"styleRecalcs": 76,
"styleRecalcDurationMs": 42.263999999999996,
"layouts": 12,
"layoutDurationMs": 5.856,
"taskDurationMs": 864.0139999999999,
"heapDeltaBytes": -12506500,
"heapUsedBytes": 56896964,
"domNodes": -237,
"jsHeapTotalBytes": 19558400,
"scriptDurationMs": 116.20899999999999,
"eventListeners": -199,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.670000000000012,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "canvas-zoom-sweep",
"durationMs": 1744.6149999999534,
"styleRecalcs": 31,
"styleRecalcDurationMs": 18.281,
"layouts": 6,
"layoutDurationMs": 0.912,
"taskDurationMs": 372.607,
"heapDeltaBytes": 5236776,
"heapUsedBytes": 56501220,
"domNodes": -241,
"jsHeapTotalBytes": 1961984,
"scriptDurationMs": 22.467000000000002,
"eventListeners": -186,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "canvas-zoom-sweep",
"durationMs": 1742.4650000000383,
"styleRecalcs": 31,
"styleRecalcDurationMs": 17.438999999999997,
"layouts": 6,
"layoutDurationMs": 0.662,
"taskDurationMs": 371.77,
"heapDeltaBytes": 8921008,
"heapUsedBytes": 60859144,
"domNodes": -240,
"jsHeapTotalBytes": 5369856,
"scriptDurationMs": 22.164999999999996,
"eventListeners": -182,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "dom-widget-clipping",
"durationMs": 626.7080000000078,
"styleRecalcs": 11,
"styleRecalcDurationMs": 7.882,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 387.828,
"heapDeltaBytes": 12803364,
"heapUsedBytes": 64117484,
"domNodes": -291,
"jsHeapTotalBytes": 3796992,
"scriptDurationMs": 53.062,
"eventListeners": -203,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666682,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "dom-widget-clipping",
"durationMs": 616.4499999999862,
"styleRecalcs": 11,
"styleRecalcDurationMs": 9.32,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 405.46000000000004,
"heapDeltaBytes": -23675568,
"heapUsedBytes": 45711992,
"domNodes": -283,
"jsHeapTotalBytes": 6713344,
"scriptDurationMs": 53.394000000000005,
"eventListeners": -203,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666682,
"p95FrameDurationMs": 16.700000000000273
},
{
"name": "large-graph-idle",
"durationMs": 2040.5719999999974,
"styleRecalcs": 9,
"styleRecalcDurationMs": 8.121000000000002,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 618.231,
"heapDeltaBytes": -25364016,
"heapUsedBytes": 58676268,
"domNodes": -263,
"jsHeapTotalBytes": 2445312,
"scriptDurationMs": 94.40400000000001,
"eventListeners": -199,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "large-graph-idle",
"durationMs": 2042.1390000000201,
"styleRecalcs": 8,
"styleRecalcDurationMs": 9.082999999999998,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 604.143,
"heapDeltaBytes": -25091840,
"heapUsedBytes": 58813384,
"domNodes": -266,
"jsHeapTotalBytes": 1658880,
"scriptDurationMs": 94.295,
"eventListeners": -199,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333335,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "large-graph-pan",
"durationMs": 2120.7019999999943,
"styleRecalcs": 69,
"styleRecalcDurationMs": 17.716,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 1128.627,
"heapDeltaBytes": -1991508,
"heapUsedBytes": 83231636,
"domNodes": -267,
"jsHeapTotalBytes": 3174400,
"scriptDurationMs": 384.027,
"eventListeners": -197,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "large-graph-pan",
"durationMs": 2071.9870000000356,
"styleRecalcs": 66,
"styleRecalcDurationMs": 15.324000000000002,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 1041.991,
"heapDeltaBytes": 2323184,
"heapUsedBytes": 54750596,
"domNodes": -282,
"jsHeapTotalBytes": 0,
"scriptDurationMs": 368.344,
"eventListeners": 6,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "large-graph-zoom",
"durationMs": 3108.7679999999978,
"styleRecalcs": 65,
"styleRecalcDurationMs": 18.186,
"layouts": 60,
"layoutDurationMs": 7.729999999999999,
"taskDurationMs": 1297.6460000000002,
"heapDeltaBytes": 13904840,
"heapUsedBytes": 67245032,
"domNodes": -279,
"jsHeapTotalBytes": 786432,
"scriptDurationMs": 465.991,
"eventListeners": -195,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333335,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "large-graph-zoom",
"durationMs": 3100.337999999965,
"styleRecalcs": 63,
"styleRecalcDurationMs": 17.645999999999997,
"layouts": 60,
"layoutDurationMs": 8.078999999999999,
"taskDurationMs": 1300.944,
"heapDeltaBytes": 12733236,
"heapUsedBytes": 66167644,
"domNodes": -283,
"jsHeapTotalBytes": 0,
"scriptDurationMs": 453.575,
"eventListeners": 8,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333332,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "minimap-idle",
"durationMs": 2039.3700000000194,
"styleRecalcs": 9,
"styleRecalcDurationMs": 8.414000000000001,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 557.515,
"heapDeltaBytes": -16705284,
"heapUsedBytes": 58683728,
"domNodes": -270,
"jsHeapTotalBytes": 4018176,
"scriptDurationMs": 89.215,
"eventListeners": -199,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "minimap-idle",
"durationMs": 2044.9699999999211,
"styleRecalcs": 9,
"styleRecalcDurationMs": 8.639,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 557.4520000000001,
"heapDeltaBytes": -8868848,
"heapUsedBytes": 64289712,
"domNodes": 18,
"jsHeapTotalBytes": 8794112,
"scriptDurationMs": 93.837,
"eventListeners": 4,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "subgraph-dom-widget-clipping",
"durationMs": 618.852000000004,
"styleRecalcs": 49,
"styleRecalcDurationMs": 14.967999999999998,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 427.99,
"heapDeltaBytes": 3212048,
"heapUsedBytes": 72771340,
"domNodes": -273,
"jsHeapTotalBytes": 6189056,
"scriptDurationMs": 124.20699999999998,
"eventListeners": -193,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000273
},
{
"name": "subgraph-dom-widget-clipping",
"durationMs": 605.5020000000013,
"styleRecalcs": 47,
"styleRecalcDurationMs": 12.039000000000001,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 406.41600000000005,
"heapDeltaBytes": 10530064,
"heapUsedBytes": 63059232,
"domNodes": -289,
"jsHeapTotalBytes": 6418432,
"scriptDurationMs": 121.531,
"eventListeners": -195,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66999999999998,
"p95FrameDurationMs": 16.799999999999727
},
{
"name": "subgraph-idle",
"durationMs": 2032.1739999999977,
"styleRecalcs": 11,
"styleRecalcDurationMs": 9.972,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 457.31699999999995,
"heapDeltaBytes": -8288532,
"heapUsedBytes": 61187248,
"domNodes": -275,
"jsHeapTotalBytes": 19820544,
"scriptDurationMs": 18.452,
"eventListeners": -199,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "subgraph-idle",
"durationMs": 2056.9550000000163,
"styleRecalcs": 10,
"styleRecalcDurationMs": 10.564,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 453.966,
"heapDeltaBytes": -8128440,
"heapUsedBytes": 61529168,
"domNodes": -280,
"jsHeapTotalBytes": 20344832,
"scriptDurationMs": 18.520999999999997,
"eventListeners": -199,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "subgraph-mouse-sweep",
"durationMs": 1734.3340000000467,
"styleRecalcs": 77,
"styleRecalcDurationMs": 41.678,
"layouts": 16,
"layoutDurationMs": 4.34,
"taskDurationMs": 780.643,
"heapDeltaBytes": -16656148,
"heapUsedBytes": 52799032,
"domNodes": -233,
"jsHeapTotalBytes": 19558400,
"scriptDurationMs": 90.239,
"eventListeners": -199,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.699999999999818
},
{
"name": "subgraph-mouse-sweep",
"durationMs": 1740.4630000000907,
"styleRecalcs": 77,
"styleRecalcDurationMs": 49.961000000000006,
"layouts": 16,
"layoutDurationMs": 4.550999999999999,
"taskDurationMs": 791.2620000000001,
"heapDeltaBytes": -16938388,
"heapUsedBytes": 52660184,
"domNodes": -237,
"jsHeapTotalBytes": 19820544,
"scriptDurationMs": 90.054,
"eventListeners": -199,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "subgraph-transition-enter",
"durationMs": 915.4679999999757,
"styleRecalcs": 17,
"styleRecalcDurationMs": 24.485999999999994,
"layouts": 4,
"layoutDurationMs": 11.229,
"taskDurationMs": 690.0619999999999,
"heapDeltaBytes": 4401252,
"heapUsedBytes": 80770368,
"domNodes": 13833,
"jsHeapTotalBytes": 16515072,
"scriptDurationMs": 26.456000000000007,
"eventListeners": 2531,
"totalBlockingTimeMs": 140,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "viewport-pan-sweep",
"durationMs": 8164.821999999958,
"styleRecalcs": 252,
"styleRecalcDurationMs": 57.085,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 4027.278,
"heapDeltaBytes": -15097008,
"heapUsedBytes": 69145580,
"domNodes": -263,
"jsHeapTotalBytes": 5795840,
"scriptDurationMs": 1255.071,
"eventListeners": -183,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333332,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "viewport-pan-sweep",
"durationMs": 8169.016000000056,
"styleRecalcs": 251,
"styleRecalcDurationMs": 57.18000000000001,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 3979.271,
"heapDeltaBytes": -383364,
"heapUsedBytes": 66968768,
"domNodes": -272,
"jsHeapTotalBytes": 1777664,
"scriptDurationMs": 1242.553,
"eventListeners": -183,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333332,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "vue-large-graph-idle",
"durationMs": 10665.481999999996,
"styleRecalcs": 0,
"styleRecalcDurationMs": 0,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 10651.139000000001,
"heapDeltaBytes": -18010156,
"heapUsedBytes": 175897708,
"domNodes": -3300,
"jsHeapTotalBytes": 11272192,
"scriptDurationMs": 522.519,
"eventListeners": -16370,
"totalBlockingTimeMs": 0,
"frameDurationMs": 17.223333333333358,
"p95FrameDurationMs": 16.80000000000291
},
{
"name": "vue-large-graph-idle",
"durationMs": 10842.283000000065,
"styleRecalcs": 0,
"styleRecalcDurationMs": 0,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 10819.697999999999,
"heapDeltaBytes": -29303704,
"heapUsedBytes": 171727128,
"domNodes": -3300,
"jsHeapTotalBytes": 10223616,
"scriptDurationMs": 541.687,
"eventListeners": -16368,
"totalBlockingTimeMs": 0,
"frameDurationMs": 17.776666666666642,
"p95FrameDurationMs": 16.80000000000291
},
{
"name": "vue-large-graph-pan",
"durationMs": 12748.585999999988,
"styleRecalcs": 65,
"styleRecalcDurationMs": 22.70400000000003,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 12728.648999999998,
"heapDeltaBytes": -7132252,
"heapUsedBytes": 182286848,
"domNodes": -3291,
"jsHeapTotalBytes": 17477632,
"scriptDurationMs": 822.6260000000001,
"eventListeners": -12869,
"totalBlockingTimeMs": 39,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "vue-large-graph-pan",
"durationMs": 12837.259000000016,
"styleRecalcs": 65,
"styleRecalcDurationMs": 19.50300000000002,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 12809.736,
"heapDeltaBytes": -52671172,
"heapUsedBytes": 165032256,
"domNodes": -3300,
"jsHeapTotalBytes": 17039360,
"scriptDurationMs": 873.3760000000002,
"eventListeners": -16366,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "workflow-execution",
"durationMs": 486.53600000000097,
"styleRecalcs": 14,
"styleRecalcDurationMs": 25.442,
"layouts": 5,
"layoutDurationMs": 1.6150000000000002,
"taskDurationMs": 226.20100000000002,
"heapDeltaBytes": -21767300,
"heapUsedBytes": 48662060,
"domNodes": -170,
"jsHeapTotalBytes": 5140480,
"scriptDurationMs": 17.481000000000005,
"eventListeners": -132,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000273
},
{
"name": "workflow-execution",
"durationMs": 454.32099999993625,
"styleRecalcs": 17,
"styleRecalcDurationMs": 22.009999999999994,
"layouts": 4,
"layoutDurationMs": 1.073,
"taskDurationMs": 114.13799999999999,
"heapDeltaBytes": 4951448,
"heapUsedBytes": 58174864,
"domNodes": 157,
"jsHeapTotalBytes": 262144,
"scriptDurationMs": 16.266000000000002,
"eventListeners": 69,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666682,
"p95FrameDurationMs": 16.800000000000182
}
]
} |
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughThis PR threads explicit ChangesSubscription attribution and checkout metadata
Estimated code review effort: 4 (Complex) | ~45 minutes Possibly related issues
Possibly related PRs
Suggested labels: Suggested reviewers: Caution Pre-merge checks failedPlease resolve all errors before merging. Addressing warnings is optional.
❌ Failed checks (1 error)
✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/platform/cloud/subscription/composables/useSubscriptionDialog.ts (1)
56-100: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
modal_openedfires even when the pricing table isn't shown.
trackModalOpened(options?.reason)runs unconditionally at the top ofshowPricingTable(Line 70), before the member-restriction check (Lines 80-100). When a team member without manage-subscription permission triggers this path, the actual dialog rendered isSubscriptionInactiveMemberDialog(the "ask your owner" modal) — not the pricing table. Yet themodal_openedevent still records as if the paywall/pricing dialog opened, per the docstring's stated purpose (Lines 15-19). This skews paywall funnel analytics.🐛 Proposed fix: move tracking after the early-return branch
function showPricingTable(options?: SubscriptionDialogOptions) { if (!isCloud) return - trackModalOpened(options?.reason) - // Resolved lazily (not at setup): useWorkspaceUI reads useBillingContext, so // a setup-time read re-enters the half-built context during the // useBillingContext -> useWorkspaceBilling -> useSubscriptionDialog cycle. const { permissions } = useWorkspaceUI() // Members can't manage the workspace subscription, so a blocked run shows a // small read-only "ask your owner to reactivate" modal instead of the // pricing table. Out-of-credits still routes everyone to the credits flow. if ( flags.teamWorkspacesEnabled && !workspaceStore.isInPersonalWorkspace && !permissions.value.canManageSubscription && options?.reason !== 'out_of_credits' ) { dialogService.showLayoutDialog({ key: DIALOG_KEY, component: defineAsyncComponent( () => import('`@/platform/workspace/components/SubscriptionInactiveMemberDialog.vue`') ), props: { onClose: hide }, dialogComponentProps: { renderer: 'reka', contentClass: 'w-[min(360px,95vw)] max-w-[min(360px,95vw)] sm:max-w-[min(360px,95vw)] border-0 bg-transparent shadow-none' } }) return } + trackModalOpened(options?.reason) + // Shared dialog shell styling for both variants.🤖 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/cloud/subscription/composables/useSubscriptionDialog.ts` around lines 56 - 100, The `modal_opened` telemetry in `showPricingTable` is firing before it’s known whether the pricing table will actually render. Move the `trackModalOpened(options?.reason)` call in `useSubscriptionDialog` to after the member-restriction early return so it only runs when the pricing table is shown, and keep the `SubscriptionInactiveMemberDialog` branch untracked as a pricing-table open.
🤖 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/useSubscriptionCheckout.ts`:
- Around line 313-314: The team checkout telemetry is inferring checkout type
from previewData in the trackCheckoutStarted call, which can misclassify real
team upgrades as "new". Update the team flow in useSubscriptionCheckout,
especially handleSubscribeTeamClick and the trackCheckoutStarted('team', ...)
call, to pass the explicit checkout type determined by the team-change path
instead of relying on previewData. Preserve the existing team checkout type
through the subscribe flow so the telemetry reflects the actual upgrade vs new
purchase state.
---
Outside diff comments:
In `@src/platform/cloud/subscription/composables/useSubscriptionDialog.ts`:
- Around line 56-100: The `modal_opened` telemetry in `showPricingTable` is
firing before it’s known whether the pricing table will actually render. Move
the `trackModalOpened(options?.reason)` call in `useSubscriptionDialog` to after
the member-restriction early return so it only runs when the pricing table is
shown, and keep the `SubscriptionInactiveMemberDialog` branch untracked as a
pricing-table open.
🪄 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: 8ab76a80-3228-4955-8e70-40fb37ac6bb9
📒 Files selected for processing (40)
src/components/topbar/CurrentUserPopoverLegacy.vuesrc/composables/billing/types.tssrc/composables/billing/useBillingContext.tssrc/composables/billing/useLegacyBilling.tssrc/composables/useCoreCommands.tssrc/platform/assets/components/UploadModelUpgradeModal.vuesrc/platform/cloud/onboarding/CloudSubscriptionRedirectView.test.tssrc/platform/cloud/onboarding/CloudSubscriptionRedirectView.vuesrc/platform/cloud/subscription/components/CreditsTile.vuesrc/platform/cloud/subscription/components/PricingTable.vuesrc/platform/cloud/subscription/components/SubscribeButton.vuesrc/platform/cloud/subscription/components/SubscribeToRun.vuesrc/platform/cloud/subscription/components/SubscriptionPanelContentLegacy.vuesrc/platform/cloud/subscription/components/SubscriptionRequiredDialogContent.vuesrc/platform/cloud/subscription/composables/usePricingTableUrlLoader.test.tssrc/platform/cloud/subscription/composables/usePricingTableUrlLoader.tssrc/platform/cloud/subscription/composables/useSubscription.tssrc/platform/cloud/subscription/composables/useSubscriptionActions.test.tssrc/platform/cloud/subscription/composables/useSubscriptionActions.tssrc/platform/cloud/subscription/composables/useSubscriptionDialog.test.tssrc/platform/cloud/subscription/composables/useSubscriptionDialog.tssrc/platform/cloud/subscription/utils/subscriptionCheckoutTracker.tssrc/platform/cloud/subscription/utils/subscriptionCheckoutUtil.test.tssrc/platform/cloud/subscription/utils/subscriptionCheckoutUtil.tssrc/platform/cloud/subscription/utils/teamSubscriptionCheckoutUtil.test.tssrc/platform/cloud/subscription/utils/teamSubscriptionCheckoutUtil.tssrc/platform/telemetry/TelemetryRegistry.tssrc/platform/telemetry/providers/cloud/PostHogTelemetryProvider.test.tssrc/platform/telemetry/providers/cloud/PostHogTelemetryProvider.tssrc/platform/telemetry/providers/host/HostTelemetrySink.tssrc/platform/telemetry/types.tssrc/platform/workspace/components/CurrentUserPopoverWorkspace.vuesrc/platform/workspace/components/SubscriptionPanelContentWorkspace.vuesrc/platform/workspace/components/SubscriptionRequiredDialogContentUnified.vuesrc/platform/workspace/components/dialogs/InviteMemberUpsellDialogContent.vuesrc/platform/workspace/composables/useMembersPanel.tssrc/platform/workspace/composables/useSubscriptionCheckout.test.tssrc/platform/workspace/composables/useSubscriptionCheckout.tssrc/platform/workspace/composables/useWorkspaceBilling.tssrc/services/dialogService.ts
💤 Files with no reviewable changes (2)
- src/platform/cloud/subscription/composables/usePricingTableUrlLoader.ts
- src/platform/cloud/subscription/composables/usePricingTableUrlLoader.test.ts
The free-tier dialog only rendered its description and credits-refresh line for the pre-existing reasons, so the widened intent taxonomy made them vanish (caught by the cloud e2e suite). Gate the special copy on the two credit-blocked variants instead, and add coverage for the new telemetry dispatch paths.
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/platform/workspace/composables/useSubscriptionCheckout.test.ts`:
- Around line 621-637: The test leaves shared state cleanup inside the test
body, so a failure can leak `mockUserId.value = null` into later cases. Move the
`mockUserId` reset out of the `it('skips begin_checkout when no user id is
available')` block and into shared setup/teardown for
`useSubscriptionCheckout.test.ts`, using `beforeEach` or `afterEach` around the
`setup`/`handleAddCreditCard` tests so `mockUserId` is always restored.
🪄 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: 0a933c9d-bce2-4244-8ba4-985c1a55b926
📒 Files selected for processing (6)
src/platform/cloud/subscription/components/FreeTierDialogContent.test.tssrc/platform/cloud/subscription/components/FreeTierDialogContent.vuesrc/platform/cloud/subscription/utils/subscriptionCheckoutTracker.test.tssrc/platform/telemetry/TelemetryRegistry.test.tssrc/platform/telemetry/providers/host/HostTelemetrySink.test.tssrc/platform/workspace/composables/useSubscriptionCheckout.test.ts
| extends Record<string, unknown>, CheckoutAttributionMetadata { | ||
| user_id: string | ||
| tier: TierKey | ||
| tier: TierKey | 'team' |
There was a problem hiding this comment.
Fine as is, but worth keeping an eye on.
team doesn't really belong in TierKey, but may warrant a dedicated union type in the future.
Will need to rereview in a moment
|
Sorry @AustinMroz, I wanted to make some more improvements and additions. |
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (1)
src/platform/workspace/composables/useSubscriptionCheckout.ts (1)
222-246: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick winTelemetry call shares try/catch with
subscribe()— see root-cause comment onworkspaceCheckoutTelemetry.ts.
trackWorkspaceCheckoutStarted(...)is invoked inside the sametryblock that also awaitssubscribe(...). If the telemetry call throws,catch (error) { showSubscribeError(error) }runs andhandleSubscribeResponse(response)is skipped — surfacing a "subscribe failed" toast for a checkout that actually succeeded. This pattern is duplicated in bothhandleSubscriptionandhandleTeamSubscription. See the fix proposed onworkspaceCheckoutTelemetry.ts.Also applies to: 304-343
🤖 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/composables/useSubscriptionCheckout.ts` around lines 222 - 246, The `handleSubscription` and `handleTeamSubscription` flows in `useSubscriptionCheckout` are treating `trackWorkspaceCheckoutStarted(...)` as part of the subscription failure path, so a telemetry exception can wrongly trigger `showSubscribeError(error)` and skip `handleSubscribeResponse(response)`. Move the telemetry call out of the same `try` that awaits `subscribe(...)`, or wrap it in its own non-fatal error handling so checkout success still proceeds even if tracking fails. Keep the subscription/response handling logic centered around `subscribe`, `handleSubscribeResponse`, and `showSubscribeError`.
🤖 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/cloud/subscription/utils/subscriptionCheckoutUtil.ts`:
- Around line 125-148: The begin-checkout metadata payload is being assembled
inline in both subscriptionCheckoutUtil and PricingTable.vue, which risks the
two call sites drifting apart. Extract the shared `BeginCheckoutMetadata`
construction into a common helper such as `buildBeginCheckoutMetadata`, and have
`withPendingCheckoutAttemptId`/`trackBeginCheckout` call it so `user_id`,
`tier`, `cycle`, `checkout_type`, optional `payment_intent_source`, and
`checkoutAttribution` stay consistent across both places.
---
Duplicate comments:
In `@src/platform/workspace/composables/useSubscriptionCheckout.ts`:
- Around line 222-246: The `handleSubscription` and `handleTeamSubscription`
flows in `useSubscriptionCheckout` are treating
`trackWorkspaceCheckoutStarted(...)` as part of the subscription failure path,
so a telemetry exception can wrongly trigger `showSubscribeError(error)` and
skip `handleSubscribeResponse(response)`. Move the telemetry call out of the
same `try` that awaits `subscribe(...)`, or wrap it in its own non-fatal error
handling so checkout success still proceeds even if tracking fails. Keep the
subscription/response handling logic centered around `subscribe`,
`handleSubscribeResponse`, and `showSubscribeError`.
🪄 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: 30245a7b-327b-4faf-a5ba-70308b6e54ff
📒 Files selected for processing (19)
src/platform/cloud/onboarding/CloudSubscriptionRedirectView.test.tssrc/platform/cloud/onboarding/CloudSubscriptionRedirectView.vuesrc/platform/cloud/subscription/components/FreeTierDialogContent.test.tssrc/platform/cloud/subscription/components/FreeTierDialogContent.vuesrc/platform/cloud/subscription/components/PricingTable.vuesrc/platform/cloud/subscription/components/SubscriptionRequiredDialogContent.vuesrc/platform/cloud/subscription/composables/useSubscriptionDialog.tssrc/platform/cloud/subscription/utils/subscriptionCheckoutTracker.tssrc/platform/cloud/subscription/utils/subscriptionCheckoutUtil.test.tssrc/platform/cloud/subscription/utils/subscriptionCheckoutUtil.tssrc/platform/cloud/subscription/utils/teamSubscriptionCheckoutUtil.test.tssrc/platform/cloud/subscription/utils/teamSubscriptionCheckoutUtil.tssrc/platform/telemetry/types.tssrc/platform/workspace/components/SubscriptionRequiredDialogContentUnified.vuesrc/platform/workspace/components/SubscriptionRequiredDialogContentWorkspace.test.tssrc/platform/workspace/components/SubscriptionRequiredDialogContentWorkspace.vuesrc/platform/workspace/composables/useSubscriptionCheckout.test.tssrc/platform/workspace/composables/useSubscriptionCheckout.tssrc/platform/workspace/utils/workspaceCheckoutTelemetry.ts
| const pendingAttempt = createPendingSubscriptionCheckoutAttempt({ | ||
| tier: tierKey, | ||
| cycle: currentBillingCycle, | ||
| checkout_type: 'new', | ||
| payment_intent_source: paymentIntentSource | ||
| }) | ||
|
|
||
| if (userId.value) { | ||
| telemetry?.trackBeginCheckout({ | ||
| user_id: userId.value, | ||
| tier: tierKey, | ||
| cycle: currentBillingCycle, | ||
| checkout_type: 'new', | ||
| ...checkoutAttribution | ||
| }) | ||
| telemetry?.trackBeginCheckout( | ||
| withPendingCheckoutAttemptId( | ||
| { | ||
| user_id: userId.value, | ||
| tier: tierKey, | ||
| cycle: currentBillingCycle, | ||
| checkout_type: 'new', | ||
| ...(paymentIntentSource | ||
| ? { payment_intent_source: paymentIntentSource } | ||
| : {}), | ||
| ...checkoutAttribution | ||
| }, | ||
| pendingAttempt | ||
| ) | ||
| ) | ||
| } |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win
Consider extracting shared BeginCheckoutMetadata builder.
This begin-checkout payload construction (user_id, tier, cycle, checkout_type, conditional payment_intent_source, spread checkoutAttribution) is duplicated almost identically in PricingTable.vue's beginCheckoutMetadata. A small shared helper (e.g., buildBeginCheckoutMetadata) would reduce drift risk between the two call sites, especially given the previous_cycle gap flagged in PricingTable.vue.
🤖 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/cloud/subscription/utils/subscriptionCheckoutUtil.ts` around
lines 125 - 148, The begin-checkout metadata payload is being assembled inline
in both subscriptionCheckoutUtil and PricingTable.vue, which risks the two call
sites drifting apart. Extract the shared `BeginCheckoutMetadata` construction
into a common helper such as `buildBeginCheckoutMetadata`, and have
`withPendingCheckoutAttemptId`/`trackBeginCheckout` call it so `user_id`,
`tier`, `cycle`, `checkout_type`, optional `payment_intent_source`, and
`checkoutAttribution` stay consistent across both places.
Not unless Austin asks for it
AustinMroz
left a comment
There was a problem hiding this comment.
Changes are pretty significant, but still get my approval.
Summary
Answers "why did this user want to pay?" by capturing the triggering product moment at every paywall/upsell entry point and carrying it through checkout and success telemetry.
Changes
SubscriptionDialogReasonfrom 4 coarse values to 13 grounded intent sources (subscribe_to_run,upgrade_to_add_credits,invite_member_upsell,settings_billing_panel, etc.)app:subscription_required_modal_openedfromuseSubscriptionDialog(the choke point all dialog variants pass through) — the workspace/unified path previously emitted nothing; remove the now-duplicate emitters inuseSubscriptionandusePricingTableUrlLoaderpayment_intent_sourcetoBeginCheckoutMetadata/SubscriptionSuccessMetadata, threaded via the existingreasonprop: dialog →PricingTable→performSubscriptionCheckout→ pending-attempt record, so legacyapp:monthly_subscription_succeededcarries intent alongsidecheckout_attempt_idbegin_checkouton the workspace checkout path (useSubscriptionCheckout, personal + team confirm) and the team deep-link util — both previously emitted nothing;tierwidened toTierKey | 'team'trackBeginCheckoutinPostHogTelemetryProvider(was GTM/host-only, sobegin_checkoutnever reached PostHog)showSubscriptionDialog(options)through the billing-context adapters and pass a reason at ~14 call sites; addsourcetoapp:add_api_credit_button_clickedReview Focus
modal_openednow fires once per dialog actually shown, so a free-tier user clicking Upgrade emits two events (free-tier dialog, then pricing table) where the legacy path emitted oneuseSubscriptionCheckoutgained an optional second parameter