fix: gate payment recovery banners on v1PaymentRecovery only - #14918
fix: gate payment recovery banners on v1PaymentRecovery only#14918dante01yoon wants to merge 10 commits into
Conversation
Co-authored-by: Amp <amp@ampcode.com> Amp-Thread-ID: https://ampcode.com/threads/T-019fc867-33d7-7463-b4d0-ab1c1cb297de
Amp-Thread-ID: https://ampcode.com/threads/T-019fc867-33d7-7463-b4d0-ab1c1cb297de Co-authored-by: Amp <amp@ampcode.com>
Amp-Thread-ID: https://ampcode.com/threads/T-019fc867-33d7-7463-b4d0-ab1c1cb297de Co-authored-by: Amp <amp@ampcode.com>
Amp-Thread-ID: https://ampcode.com/threads/T-019fc867-33d7-7463-b4d0-ab1c1cb297de Co-authored-by: Amp <amp@ampcode.com>
Amp-Thread-ID: https://ampcode.com/threads/T-019fc867-33d7-7463-b4d0-ab1c1cb297de Co-authored-by: Amp <amp@ampcode.com>
Amp-Thread-ID: https://ampcode.com/threads/T-019fdd9b-6402-755a-be5b-5e59c15658a7 Co-authored-by: Amp <amp@ampcode.com>
🎭 Playwright: ✅ 1784 passed, 0 failed · 4 flaky📊 Browser Reports
🎨 Storybook: ✅ Built — View Storybook📦 Bundle: 8.63 MB gzip 🔴 +1.44 kBDetailsSummary
Category Glance App Entry Points — 3.67 kB (baseline 3.67 kB) • ⚪ 0 BMain entry bundles and manifests
Status: 1 added / 1 removed Graph Workspace — 1.36 MB (baseline 1.36 MB) • ⚪ 0 BGraph editor runtime, canvas, workflow orchestration
Status: 2 added / 2 removed / 1 unchanged Views & Navigation — 124 kB (baseline 124 kB) • ⚪ 0 BTop-level views, pages, and routed surfaces
Status: 13 added / 13 removed / 4 unchanged Panels & Settings — 570 kB (baseline 570 kB) • ⚪ 0 BConfiguration panels, inspectors, and settings screens
Status: 10 added / 10 removed / 16 unchanged User & Accounts — 27 kB (baseline 27 kB) • ⚪ 0 BAuthentication, profile, and account management bundles
Status: 6 added / 6 removed / 4 unchanged Editors & Dialogs — 125 kB (baseline 125 kB) • ⚪ 0 BModals, dialogs, drawers, and in-app editors
Status: 7 added / 7 removed / 1 unchanged UI Components — 67.3 kB (baseline 67.1 kB) • 🔴 +167 BReusable component library chunks
Status: 6 added / 6 removed / 8 unchanged Data & Services — 3.49 MB (baseline 3.49 MB) • 🔴 +1.6 kBStores, services, APIs, and repositories
Status: 14 added / 14 removed / 3 unchanged Utilities & Hooks — 547 kB (baseline 547 kB) • ⚪ 0 BHelpers, composables, and utility bundles
Status: 21 added / 21 removed / 17 unchanged Vendor & Third-Party — 16.3 MB (baseline 16.3 MB) • ⚪ 0 BExternal libraries and shared vendor chunks Status: 17 unchanged Other — 13.8 MB (baseline 13.8 MB) • 🔴 +4.3 kBBundles that do not match a named category
Status: 76 added / 76 removed / 210 unchanged ⚡ Performance Report
Show regressions
All metrics
Historical variance (last 15 runs)
Trend (last 15 commits on main)
Raw data{
"timestamp": "2026-08-08T00:38:46.511Z",
"gitSha": "3e877bf62d84dd2f07c0528b5aba507a9bf3125c",
"branch": "dante01yoon/gate-billing-banner-on-payment-recovery",
"measurements": [
{
"name": "canvas-idle",
"durationMs": 2089.244999999977,
"styleRecalcs": 11,
"styleRecalcDurationMs": 7.994,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 515.0559999999999,
"heapDeltaBytes": -17809036,
"heapUsedBytes": 46473580,
"domNodes": -280,
"jsHeapTotalBytes": 23044096,
"scriptDurationMs": 15.926000000000002,
"eventListeners": -153,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "canvas-idle",
"durationMs": 2027.5649999999814,
"styleRecalcs": 10,
"styleRecalcDurationMs": 7.099000000000001,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 451.415,
"heapDeltaBytes": 6040792,
"heapUsedBytes": 70241108,
"domNodes": 20,
"jsHeapTotalBytes": 25165824,
"scriptDurationMs": 14.889,
"eventListeners": 4,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66999999999998,
"p95FrameDurationMs": 16.699999999999818
},
{
"name": "canvas-mouse-sweep",
"durationMs": 2169.202000000013,
"styleRecalcs": 84,
"styleRecalcDurationMs": 47.022000000000006,
"layouts": 12,
"layoutDurationMs": 10.021,
"taskDurationMs": 1100.453,
"heapDeltaBytes": -10689712,
"heapUsedBytes": 53787292,
"domNodes": -283,
"jsHeapTotalBytes": 24092672,
"scriptDurationMs": 126.35200000000002,
"eventListeners": -151,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "canvas-mouse-sweep",
"durationMs": 1807.992000000013,
"styleRecalcs": 73,
"styleRecalcDurationMs": 31.887,
"layouts": 12,
"layoutDurationMs": 3.315,
"taskDurationMs": 862.1020000000001,
"heapDeltaBytes": -14930100,
"heapUsedBytes": 49487952,
"domNodes": -281,
"jsHeapTotalBytes": 23830528,
"scriptDurationMs": 112.51899999999999,
"eventListeners": -153,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.699999999999818
},
{
"name": "canvas-zoom-sweep",
"durationMs": 1739.6320000000287,
"styleRecalcs": 31,
"styleRecalcDurationMs": 15.548999999999998,
"layouts": 6,
"layoutDurationMs": 0.6020000000000001,
"taskDurationMs": 366.68999999999994,
"heapDeltaBytes": 8624104,
"heapUsedBytes": 73092692,
"domNodes": 78,
"jsHeapTotalBytes": 24379392,
"scriptDurationMs": 18.169999999999998,
"eventListeners": 19,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "canvas-zoom-sweep",
"durationMs": 1725.1810000000205,
"styleRecalcs": 31,
"styleRecalcDurationMs": 15.415,
"layouts": 6,
"layoutDurationMs": 0.639,
"taskDurationMs": 358.751,
"heapDeltaBytes": 8806008,
"heapUsedBytes": 73110644,
"domNodes": 78,
"jsHeapTotalBytes": 24641536,
"scriptDurationMs": 17.903000000000002,
"eventListeners": 19,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333332,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "dom-widget-clipping",
"durationMs": 552.330000000012,
"styleRecalcs": 9,
"styleRecalcDurationMs": 5.010999999999998,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 359.70399999999995,
"heapDeltaBytes": -11207388,
"heapUsedBytes": 53071652,
"domNodes": 14,
"jsHeapTotalBytes": 24903680,
"scriptDurationMs": 52.111,
"eventListeners": 0,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333335,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "dom-widget-clipping",
"durationMs": 589.01800000001,
"styleRecalcs": 11,
"styleRecalcDurationMs": 6.950999999999999,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 389.55899999999997,
"heapDeltaBytes": -11149556,
"heapUsedBytes": 53107032,
"domNodes": 18,
"jsHeapTotalBytes": 25952256,
"scriptDurationMs": 54.79800000000001,
"eventListeners": 0,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "large-graph-idle",
"durationMs": 2011.891999999989,
"styleRecalcs": 9,
"styleRecalcDurationMs": 7.026999999999998,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 665.9809999999999,
"heapDeltaBytes": 7220888,
"heapUsedBytes": 67404284,
"domNodes": -284,
"jsHeapTotalBytes": 3772416,
"scriptDurationMs": 90.29100000000001,
"eventListeners": -149,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66999999999998,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "large-graph-idle",
"durationMs": 2039.3739999999525,
"styleRecalcs": 9,
"styleRecalcDurationMs": 6.47,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 659.9090000000001,
"heapDeltaBytes": 2299820,
"heapUsedBytes": 62251920,
"domNodes": -282,
"jsHeapTotalBytes": 4034560,
"scriptDurationMs": 90.037,
"eventListeners": -179,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "large-graph-pan",
"durationMs": 2160.8939999999848,
"styleRecalcs": 69,
"styleRecalcDurationMs": 14.160000000000002,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 1209.981,
"heapDeltaBytes": 4866304,
"heapUsedBytes": 65641288,
"domNodes": -284,
"jsHeapTotalBytes": 3702784,
"scriptDurationMs": 403.594,
"eventListeners": -147,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.699999999999818
},
{
"name": "large-graph-pan",
"durationMs": 2136.4690000000337,
"styleRecalcs": 68,
"styleRecalcDurationMs": 13.554,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 1180.045,
"heapDeltaBytes": -5694480,
"heapUsedBytes": 55513564,
"domNodes": -287,
"jsHeapTotalBytes": 5345280,
"scriptDurationMs": 388.849,
"eventListeners": -179,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333335,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "large-graph-zoom",
"durationMs": 3083.6009999999874,
"styleRecalcs": 64,
"styleRecalcDurationMs": 13.633999999999997,
"layouts": 60,
"layoutDurationMs": 7.675,
"taskDurationMs": 1387.146,
"heapDeltaBytes": 22467540,
"heapUsedBytes": 85291544,
"domNodes": 10,
"jsHeapTotalBytes": 7602176,
"scriptDurationMs": 494.37,
"eventListeners": 8,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "large-graph-zoom",
"durationMs": 3147.689000000014,
"styleRecalcs": 65,
"styleRecalcDurationMs": 14.064,
"layouts": 60,
"layoutDurationMs": 7.5169999999999995,
"taskDurationMs": 1492.5410000000002,
"heapDeltaBytes": -5896848,
"heapUsedBytes": 56354860,
"domNodes": -286,
"jsHeapTotalBytes": 6656000,
"scriptDurationMs": 534.6149999999999,
"eventListeners": -153,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333335,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "minimap-idle",
"durationMs": 2024.8859999999809,
"styleRecalcs": 7,
"styleRecalcDurationMs": 4.900999999999999,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 658.015,
"heapDeltaBytes": 6906492,
"heapUsedBytes": 67783876,
"domNodes": -285,
"jsHeapTotalBytes": 4034560,
"scriptDurationMs": 89.502,
"eventListeners": -149,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66999999999998,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "minimap-idle",
"durationMs": 2027.9239999999845,
"styleRecalcs": 9,
"styleRecalcDurationMs": 6.906000000000002,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 660.6569999999998,
"heapDeltaBytes": 6549332,
"heapUsedBytes": 67178800,
"domNodes": -282,
"jsHeapTotalBytes": 3772416,
"scriptDurationMs": 90.76600000000002,
"eventListeners": -149,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.699999999999818
},
{
"name": "subgraph-dom-widget-clipping",
"durationMs": 565.9110000000283,
"styleRecalcs": 47,
"styleRecalcDurationMs": 9.07,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 383.994,
"heapDeltaBytes": -10560228,
"heapUsedBytes": 53774940,
"domNodes": 20,
"jsHeapTotalBytes": 26214400,
"scriptDurationMs": 116.656,
"eventListeners": 6,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "subgraph-dom-widget-clipping",
"durationMs": 561.5230000000793,
"styleRecalcs": 46,
"styleRecalcDurationMs": 9.318999999999999,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 390.741,
"heapDeltaBytes": -10675500,
"heapUsedBytes": 53769644,
"domNodes": 18,
"jsHeapTotalBytes": 25952256,
"scriptDurationMs": 120.35,
"eventListeners": 6,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "subgraph-idle",
"durationMs": 1991.4329999999723,
"styleRecalcs": 10,
"styleRecalcDurationMs": 7.3809999999999985,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 445.46200000000005,
"heapDeltaBytes": 5903696,
"heapUsedBytes": 70073948,
"domNodes": 20,
"jsHeapTotalBytes": 24641536,
"scriptDurationMs": 13.174999999999999,
"eventListeners": 4,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66999999999998,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "subgraph-idle",
"durationMs": 2003.8069999999948,
"styleRecalcs": 9,
"styleRecalcDurationMs": 7.258999999999998,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 494.094,
"heapDeltaBytes": -17095788,
"heapUsedBytes": 47365036,
"domNodes": -282,
"jsHeapTotalBytes": 22519808,
"scriptDurationMs": 12.857999999999999,
"eventListeners": -153,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333335,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "subgraph-mouse-sweep",
"durationMs": 1690.0459999999953,
"styleRecalcs": 77,
"styleRecalcDurationMs": 34.194,
"layouts": 16,
"layoutDurationMs": 4.6899999999999995,
"taskDurationMs": 760.368,
"heapDeltaBytes": -16551068,
"heapUsedBytes": 47870440,
"domNodes": -282,
"jsHeapTotalBytes": 22519808,
"scriptDurationMs": 82.97800000000001,
"eventListeners": -153,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "subgraph-mouse-sweep",
"durationMs": 1757.7949999999873,
"styleRecalcs": 79,
"styleRecalcDurationMs": 34.82,
"layouts": 16,
"layoutDurationMs": 4.747000000000001,
"taskDurationMs": 783.037,
"heapDeltaBytes": -17713652,
"heapUsedBytes": 46726820,
"domNodes": -282,
"jsHeapTotalBytes": 22781952,
"scriptDurationMs": 83.33299999999998,
"eventListeners": -153,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "subgraph-transition-enter",
"durationMs": 1384.730999999988,
"styleRecalcs": 19,
"styleRecalcDurationMs": 28.993000000000006,
"layouts": 15,
"layoutDurationMs": 10.939999999999998,
"taskDurationMs": 933.9220000000001,
"heapDeltaBytes": 32882820,
"heapUsedBytes": 100572404,
"domNodes": 13673,
"jsHeapTotalBytes": 13631488,
"scriptDurationMs": 32.42399999999999,
"eventListeners": 2375,
"totalBlockingTimeMs": 113,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "viewport-pan-sweep",
"durationMs": 8217.670999999995,
"styleRecalcs": 250,
"styleRecalcDurationMs": 44.56400000000001,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 4200.825,
"heapDeltaBytes": 11249072,
"heapUsedBytes": 70791700,
"domNodes": -283,
"jsHeapTotalBytes": 4751360,
"scriptDurationMs": 1257.1689999999999,
"eventListeners": -133,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "viewport-pan-sweep",
"durationMs": 8202.751999999919,
"styleRecalcs": 250,
"styleRecalcDurationMs": 38.364,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 4205.2570000000005,
"heapDeltaBytes": 12187392,
"heapUsedBytes": 71536100,
"domNodes": -282,
"jsHeapTotalBytes": 5537792,
"scriptDurationMs": 1262.508,
"eventListeners": -133,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "vue-large-graph-idle",
"durationMs": 17267.634999999984,
"styleRecalcs": 0,
"styleRecalcDurationMs": 0,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 17247.864999999998,
"heapDeltaBytes": -42439340,
"heapUsedBytes": 165556208,
"domNodes": -8312,
"jsHeapTotalBytes": -10657792,
"scriptDurationMs": 500.26899999999995,
"eventListeners": -16389,
"totalBlockingTimeMs": 0,
"frameDurationMs": 17.780000000000047,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "vue-large-graph-idle",
"durationMs": 17541.34099999999,
"styleRecalcs": 0,
"styleRecalcDurationMs": 0,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 17520.878,
"heapDeltaBytes": -40314916,
"heapUsedBytes": 167077876,
"domNodes": -8312,
"jsHeapTotalBytes": -9871360,
"scriptDurationMs": 555.4019999999999,
"eventListeners": -16387,
"totalBlockingTimeMs": 0,
"frameDurationMs": 18.330000000000048,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "vue-large-graph-pan",
"durationMs": 20835.894999999993,
"styleRecalcs": 142,
"styleRecalcDurationMs": 17.391999999999992,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 20805.789,
"heapDeltaBytes": -49703200,
"heapUsedBytes": 176653200,
"domNodes": -8312,
"jsHeapTotalBytes": -11022336,
"scriptDurationMs": 822.416,
"eventListeners": -16383,
"totalBlockingTimeMs": 334,
"frameDurationMs": 17.776666666666642,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "vue-large-graph-pan",
"durationMs": 21386.17799999997,
"styleRecalcs": 150,
"styleRecalcDurationMs": 18.611000000000015,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 21343.021999999997,
"heapDeltaBytes": -36351032,
"heapUsedBytes": 170408072,
"domNodes": -8312,
"jsHeapTotalBytes": -7020544,
"scriptDurationMs": 864.794,
"eventListeners": -16381,
"totalBlockingTimeMs": 325,
"frameDurationMs": 17.780000000000047,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "workflow-execution",
"durationMs": 104.15199999999913,
"styleRecalcs": 9,
"styleRecalcDurationMs": 15.800999999999998,
"layouts": 3,
"layoutDurationMs": 1.4180000000000001,
"taskDurationMs": 85.617,
"heapDeltaBytes": 3479732,
"heapUsedBytes": 67081980,
"domNodes": 144,
"jsHeapTotalBytes": 3145728,
"scriptDurationMs": 6.6979999999999995,
"eventListeners": 49,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666682,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "workflow-execution",
"durationMs": 450.77900000001137,
"styleRecalcs": 15,
"styleRecalcDurationMs": 20.229000000000003,
"layouts": 4,
"layoutDurationMs": 1.293,
"taskDurationMs": 105.67699999999999,
"heapDeltaBytes": 5091724,
"heapUsedBytes": 68444772,
"domNodes": 123,
"jsHeapTotalBytes": 5505024,
"scriptDurationMs": 8.686000000000002,
"eventListeners": 97,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.670000000000012,
"p95FrameDurationMs": 16.800000000000182
}
]
} |
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: ASSERTIVE Plan: Pro Plus Run ID: 📒 Files selected for processing (7)
📝 WalkthroughWalkthroughPayment recovery now uses abortable billing requests, recovery-session guards, stale-response suppression, and eligibility watchers. Billing banners and run controls respond to permission or billing changes. Queue mode restores its previous value after recovery locks clear. ChangesBilling recovery lifecycle
Estimated code review effort: 4 (Complex) | ~60 minutes Sequence Diagram(s)sequenceDiagram
participant User
participant BillingStatusBanner
participant useWorkspaceBilling
participant WorkspaceAPI
User->>BillingStatusBanner: Start payment recovery
BillingStatusBanner->>useWorkspaceBilling: Manage subscription with AbortSignal
useWorkspaceBilling->>WorkspaceAPI: Request payment portal
User->>BillingStatusBanner: Disable recovery or lose eligibility
BillingStatusBanner->>useWorkspaceBilling: Abort active request
useWorkspaceBilling-->>BillingStatusBanner: Suppress stale response
Possibly related PRs
Suggested labels: Suggested reviewers: 🚥 Pre-merge checks | ✅ 6✅ Passed checks (6 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
…ing-banner-on-payment-recovery
There was a problem hiding this comment.
Actionable comments posted: 6
🤖 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/components/actionbar/ComfyRunButton/CloudRunButtonWrapper.test.ts`:
- Around line 502-507: Update the test around the second queue-button
interaction to capture the AbortSignal arguments from both manageSubscription
calls, then assert the signals are distinct and that the first signal is aborted
after the second request begins. Keep the existing request-identity and
call-count assertions.
- Around line 231-232: Extract and export the `{ key, component, props,
dialogComponentProps }` options shape used by showLayoutDialog, then type the
test’s showLayoutDialog mock with that options type so mock.calls[0][0].props
and related calls are strongly typed instead of any; use this test-specific
typed mock rather than the generic mocked service type.
In `@src/components/actionbar/ComfyRunButton/CloudRunButtonWrapper.vue`:
- Around line 125-150: Extract the repeated recovery-session validity condition
into a single predicate near the existing recovery lifecycle state, and reuse it
in the success, catch, and finally branches instead of duplicating `!isUnmounted
&& session === recoverySession && paymentRecoveryLock.value`. Preserve the
current behavior and guard ordering; do not expand the change into a composable
or replace lifecycle cleanup unless required by the implementation.
In `@src/components/actionbar/ComfyRunButton/ComfyQueueButton.test.ts`:
- Around line 216-238: Add a test alongside the existing recovery-lock cases
that renders ComfyQueueButton with an active paymentRecoveryLock, changes the
queue mode from disabled to another mode while the lock remains active, then
clears the lock and asserts that the manually selected mode is preserved rather
than restored. Target the non-restore guard in ComfyQueueButton and cover the
lock-clear path.
In
`@src/platform/workspace/components/dialogs/settings/BillingStatusBanner.test.ts`:
- Around line 332-399: Consolidate the flag rollback, permission loss, and
billing recovery cases in the recovery banner tests into an it.for table,
sharing the common manageSubscription stub, pausedState setup, click, and
AbortSignal assertion. Parameterize each invalidation action and its distinct
follow-up DOM assertion, while keeping the unmount case separate if needed
because it requires the renderBanner unmount handle.
In `@src/platform/workspace/components/dialogs/settings/BillingStatusBanner.vue`:
- Around line 199-203: Add a rejection handler to the manageSubscription promise
chain in the recovery portal flow, while retaining the existing finally cleanup
that clears recoveryPortalController only for the current controller. Ensure
failed and aborted requests are consumed without changing the cleanup behavior.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: 2dd9dc47-0709-4b05-8a45-237abe085004
📒 Files selected for processing (17)
src/components/actionbar/ComfyRunButton/CloudRunButtonWrapper.test.tssrc/components/actionbar/ComfyRunButton/CloudRunButtonWrapper.vuesrc/components/actionbar/ComfyRunButton/ComfyQueueButton.test.tssrc/components/actionbar/ComfyRunButton/ComfyQueueButton.vuesrc/composables/billing/types.tssrc/composables/billing/useBillingContext.test.tssrc/composables/billing/useBillingContext.tssrc/locales/en/main.jsonsrc/platform/workspace/api/workspaceApi.test.tssrc/platform/workspace/api/workspaceApi.tssrc/platform/workspace/components/dialogs/settings/BillingStatusBanner.test.tssrc/platform/workspace/components/dialogs/settings/BillingStatusBanner.vuesrc/platform/workspace/composables/deriveBillingBanner.test.tssrc/platform/workspace/composables/useBillingBanner.test.tssrc/platform/workspace/composables/useBillingBanner.tssrc/platform/workspace/composables/useWorkspaceBilling.test.tssrc/platform/workspace/composables/useWorkspaceBilling.ts
Amp-Thread-ID: https://ampcode.com/threads/T-019fde89-3a25-71fd-86d7-2e0815d55573 Co-authored-by: Amp <amp@ampcode.com>
|
Note GitHub couldn't provide a complete incremental comparison for this pull request, so CodeRabbit is performing a full review instead. This review may take a little longer. |
There was a problem hiding this comment.
Actionable comments posted: 3
♻️ Duplicate comments (1)
src/platform/workspace/components/dialogs/settings/BillingStatusBanner.vue (1)
189-204: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick winSurface portal failures to the user.
If
manageSubscriptionrejects for a non-abort reason, the banner stays unchanged and the user gets no feedback.CloudRunButtonWrapper.vuehandles the same call withtoastErrorHandler. Apply the same handling here, and ignoreAbortErrorso cancellation stays silent.♻️ Proposed change
+import { useErrorHandling } from '`@/composables/useErrorHandling`'+const { toastErrorHandler } = useErrorHandling()void manageSubscription(controller.signal) + .catch((error) => { + if (controller.signal.aborted) return + toastErrorHandler(error) + }) .finally(() => { if (recoveryPortalController === controller) { recoveryPortalController = null } })As per coding guidelines,
src/**/*.{js,ts,vue}must "Provide user-friendly and actionable error messages" and "Implement proper error propagation".🤖 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/dialogs/settings/BillingStatusBanner.vue` around lines 189 - 204, Update handleUpdatePayment to handle manageSubscription rejections with the existing toastErrorHandler pattern used by CloudRunButtonWrapper.vue, while suppressing AbortError cancellations. Preserve the current controller cleanup in finally and surface only non-abort failures with a user-friendly actionable message.Source: Coding guidelines
🤖 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/settings/BillingStatusBanner.test.ts`:
- Around line 332-347: Add a test near the existing manageSubscription cases
that makes state.manageSubscription reject and verifies the BillingStatusBanner
flow handles the failure without an unhandled rejection. Exercise the same user
action used by the recovery banner tests, await the rejection handling, and
assert the component remains stable without an unhandled-rejection event.
- Around line 320-330: Extend the test around payment recovery being disabled to
set the out-of-credits BillingStatus value and assert its banner still renders
with v1PaymentRecovery false. Confirm and use the exact BillingStatus enum/value
for outOfCredits, preserving the existing paymentFailed and paused assertions
and covering the non-recovery billingControlEnabled behavior.
In `@src/platform/workspace/composables/useWorkspaceBilling.test.ts`:
- Around line 967-989: Update the test “keeps an older portal response out of a
newer recovery session” so the first manageSubscription request remains active
when the second request starts; resolve the newer portal response first, then
resolve the older response, and await both requests. Assert that only the newer
URL is opened and loading ends, thereby exercising stale-response suppression
through isCurrentRequest() rather than signal.aborted.
---
Duplicate comments:
In `@src/platform/workspace/components/dialogs/settings/BillingStatusBanner.vue`:
- Around line 189-204: Update handleUpdatePayment to handle manageSubscription
rejections with the existing toastErrorHandler pattern used by
CloudRunButtonWrapper.vue, while suppressing AbortError cancellations. Preserve
the current controller cleanup in finally and surface only non-abort failures
with a user-friendly actionable message.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: f904186b-0d34-4fc2-afcc-66c6480741de
📒 Files selected for processing (17)
src/components/actionbar/ComfyRunButton/CloudRunButtonWrapper.test.tssrc/components/actionbar/ComfyRunButton/CloudRunButtonWrapper.vuesrc/components/actionbar/ComfyRunButton/ComfyQueueButton.test.tssrc/components/actionbar/ComfyRunButton/ComfyQueueButton.vuesrc/composables/billing/types.tssrc/composables/billing/useBillingContext.test.tssrc/composables/billing/useBillingContext.tssrc/locales/en/main.jsonsrc/platform/workspace/api/workspaceApi.test.tssrc/platform/workspace/api/workspaceApi.tssrc/platform/workspace/components/dialogs/settings/BillingStatusBanner.test.tssrc/platform/workspace/components/dialogs/settings/BillingStatusBanner.vuesrc/platform/workspace/composables/deriveBillingBanner.test.tssrc/platform/workspace/composables/useBillingBanner.test.tssrc/platform/workspace/composables/useBillingBanner.tssrc/platform/workspace/composables/useWorkspaceBilling.test.tssrc/platform/workspace/composables/useWorkspaceBilling.ts
Codecov Report❌ Patch coverage is @@ Coverage Diff @@
## main #14918 +/- ##
==========================================
+ Coverage 78.69% 79.85% +1.15%
==========================================
Files 1864 1865 +1
Lines 115461 122448 +6987
Branches 34795 36009 +1214
==========================================
+ Hits 90864 97780 +6916
- Misses 24064 24069 +5
- Partials 533 599 +66
Flags with carried forward coverage won't be shown. Click here to find out more.
... and 197 files with indirect coverage changes 🚀 New features to boost your workflow:
|
Amp-Thread-ID: https://ampcode.com/threads/T-019fde89-3a25-71fd-86d7-2e0815d55573 Co-authored-by: Amp <amp@ampcode.com>
Amp-Thread-ID: https://ampcode.com/threads/T-019fde89-3a25-71fd-86d7-2e0815d55573 Co-authored-by: Amp <amp@ampcode.com>
Summary
Changes
deriveBillingBannerso thatpausedandpaymentFailedbilling banners are gated exclusively onv1PaymentRecovery, notbillingControlEnabled || v1PaymentRecovery.Root cause
The recovery status branch combined two feature flags. As a result, enabling billing controls could render payment recovery banners even when the payment recovery experience itself was disabled.
AS IS
TO BE
The remaining billing banners continue to use
billingControlEnabledaccording to their existing behavior.Visual evidence
v1PaymentRecoveryOFFv1PaymentRecoveryONAS IS / flag off
TO BE / flag on
Testing
pausedandpayment_failedwith payment recovery disabled.BillingStatusBannerregression coverage.E2E coverage
No new Playwright case was added because the flag-gating contract is a deterministic derivation from mocked feature-flag, billing-status, and permission inputs. It is covered directly at both the pure derivation and rendered component boundaries; reproducing it in Playwright would repeat the same frontend mocks without exercising an additional integration boundary. The existing cloud Playwright suite passes, and the flag-off/flag-on behavior was also validated in the real app with the screenshots above.