fix(billing): scope operation completion to workspace - #15431
fix(billing): scope operation completion to workspace#15431dante01yoon wants to merge 3 commits into
Conversation
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: ASSERTIVE Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
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 |
🎨 Storybook: ✅ Built — View Storybook📦 Bundle Size
⚡ Performance Report
Absolute values
Raw data{
"timestamp": "2026-08-22T17:12:41.324Z",
"gitSha": "050904b46335b251e0b5d676741a159cb83dc0b6",
"branch": "dante/fe-1584-scope-billing-completion",
"measurements": [
{
"name": "canvas-idle",
"durationMs": 2054.7900000000254,
"styleRecalcs": 8,
"styleRecalcDurationMs": 9.042000000000002,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 427.264,
"heapDeltaBytes": 17212336,
"heapUsedBytes": 78721644,
"domNodes": -282,
"jsHeapTotalBytes": 3923968,
"scriptDurationMs": 6.571000000000001,
"eventListeners": -151,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "canvas-idle",
"durationMs": 2006.9179999999847,
"styleRecalcs": 9,
"styleRecalcDurationMs": 5.8900000000000015,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 381.655,
"heapDeltaBytes": -3371584,
"heapUsedBytes": 58044316,
"domNodes": -282,
"jsHeapTotalBytes": 4972544,
"scriptDurationMs": 5.951,
"eventListeners": -151,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "canvas-mouse-sweep",
"durationMs": 1720.119000000011,
"styleRecalcs": 71,
"styleRecalcDurationMs": 28.880000000000003,
"layouts": 12,
"layoutDurationMs": 3.439,
"taskDurationMs": 727.261,
"heapDeltaBytes": -4168624,
"heapUsedBytes": 56333572,
"domNodes": -282,
"jsHeapTotalBytes": 4710400,
"scriptDurationMs": 85.28099999999999,
"eventListeners": -149,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333332,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "canvas-mouse-sweep",
"durationMs": 1712.1980000000008,
"styleRecalcs": 71,
"styleRecalcDurationMs": 27.169,
"layouts": 12,
"layoutDurationMs": 2.979,
"taskDurationMs": 673.2230000000001,
"heapDeltaBytes": 1841252,
"heapUsedBytes": 62116544,
"domNodes": -282,
"jsHeapTotalBytes": 4448256,
"scriptDurationMs": 80.263,
"eventListeners": -151,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "canvas-zoom-sweep",
"durationMs": 1660.4839999999967,
"styleRecalcs": 31,
"styleRecalcDurationMs": 14.139000000000001,
"layouts": 6,
"layoutDurationMs": 0.642,
"taskDurationMs": 285.55299999999994,
"heapDeltaBytes": 3107424,
"heapUsedBytes": 63926056,
"domNodes": 77,
"jsHeapTotalBytes": 4718592,
"scriptDurationMs": 8.408999999999999,
"eventListeners": 19,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "canvas-zoom-sweep",
"durationMs": 1730.731999999989,
"styleRecalcs": 30,
"styleRecalcDurationMs": 13.252000000000002,
"layouts": 6,
"layoutDurationMs": 0.6169999999999999,
"taskDurationMs": 287.817,
"heapDeltaBytes": 2798932,
"heapUsedBytes": 63156900,
"domNodes": 76,
"jsHeapTotalBytes": 4718592,
"scriptDurationMs": 8.475000000000001,
"eventListeners": 19,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "dom-widget-clipping",
"durationMs": 545.880000000011,
"styleRecalcs": 11,
"styleRecalcDurationMs": 6.430999999999999,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 304.924,
"heapDeltaBytes": 9975344,
"heapUsedBytes": 70665792,
"domNodes": 18,
"jsHeapTotalBytes": 4980736,
"scriptDurationMs": 45.45300000000001,
"eventListeners": 2,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.670000000000012,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "dom-widget-clipping",
"durationMs": 467.27700000002415,
"styleRecalcs": 14,
"styleRecalcDurationMs": 7.561,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 283.922,
"heapDeltaBytes": 9980392,
"heapUsedBytes": 70659932,
"domNodes": 22,
"jsHeapTotalBytes": 4980736,
"scriptDurationMs": 43.721,
"eventListeners": 2,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000273
},
{
"name": "large-graph-idle",
"durationMs": 2026.9819999999754,
"styleRecalcs": 9,
"styleRecalcDurationMs": 7.283999999999999,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 505.65700000000004,
"heapDeltaBytes": 2337632,
"heapUsedBytes": 76421820,
"domNodes": -262,
"jsHeapTotalBytes": -2887680,
"scriptDurationMs": 13.883,
"eventListeners": -149,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.699999999999818
},
{
"name": "large-graph-idle",
"durationMs": 2020.9300000000212,
"styleRecalcs": 11,
"styleRecalcDurationMs": 6.794999999999999,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 480.095,
"heapDeltaBytes": -11227604,
"heapUsedBytes": 62958280,
"domNodes": -278,
"jsHeapTotalBytes": -2887680,
"scriptDurationMs": 12.508,
"eventListeners": -177,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "large-graph-pan",
"durationMs": 2082.3180000000434,
"styleRecalcs": 70,
"styleRecalcDurationMs": 14.043999999999999,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 894.933,
"heapDeltaBytes": 152076,
"heapUsedBytes": 75071040,
"domNodes": -260,
"jsHeapTotalBytes": -2138112,
"scriptDurationMs": 248.74800000000002,
"eventListeners": -149,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "large-graph-pan",
"durationMs": 2077.4189999999635,
"styleRecalcs": 68,
"styleRecalcDurationMs": 13.937999999999999,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 916.767,
"heapDeltaBytes": 12791824,
"heapUsedBytes": 86838608,
"domNodes": -283,
"jsHeapTotalBytes": 5201920,
"scriptDurationMs": 259.365,
"eventListeners": -179,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.699999999999818
},
{
"name": "large-graph-zoom",
"durationMs": 3170.7490000000007,
"styleRecalcs": 67,
"styleRecalcDurationMs": 14.376999999999997,
"layouts": 60,
"layoutDurationMs": 7.174999999999999,
"taskDurationMs": 1138.5149999999999,
"heapDeltaBytes": -10419944,
"heapUsedBytes": 66062752,
"domNodes": -283,
"jsHeapTotalBytes": 5500928,
"scriptDurationMs": 318.66,
"eventListeners": -153,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333332,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "large-graph-zoom",
"durationMs": 3110.3489999999283,
"styleRecalcs": 66,
"styleRecalcDurationMs": 13.948000000000002,
"layouts": 60,
"layoutDurationMs": 6.853,
"taskDurationMs": 1135.174,
"heapDeltaBytes": -13764348,
"heapUsedBytes": 61974192,
"domNodes": -285,
"jsHeapTotalBytes": 4976640,
"scriptDurationMs": 339.606,
"eventListeners": -183,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333335,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "legacy-node-drag",
"durationMs": 2121.9900000000393,
"styleRecalcs": 48,
"styleRecalcDurationMs": 10.262999999999998,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 1118.0590000000002,
"heapDeltaBytes": 9176520,
"heapUsedBytes": 86096328,
"domNodes": 16,
"jsHeapTotalBytes": 7270400,
"scriptDurationMs": 374.927,
"eventListeners": 184,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333332,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "legacy-node-drag",
"durationMs": 2090.9580000000005,
"styleRecalcs": 45,
"styleRecalcDurationMs": 7.696999999999999,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 1117.7130000000002,
"heapDeltaBytes": 1223356,
"heapUsedBytes": 63654008,
"domNodes": -268,
"jsHeapTotalBytes": 5242880,
"scriptDurationMs": 369.08599999999996,
"eventListeners": 184,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.699999999999818
},
{
"name": "minimap-idle",
"durationMs": 2051.0509999999726,
"styleRecalcs": 8,
"styleRecalcDurationMs": 5.817000000000001,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 508.54999999999995,
"heapDeltaBytes": -5282492,
"heapUsedBytes": 75303216,
"domNodes": -270,
"jsHeapTotalBytes": 3403776,
"scriptDurationMs": 13.339,
"eventListeners": -149,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66999999999998,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "minimap-idle",
"durationMs": 2036.4769999999908,
"styleRecalcs": 9,
"styleRecalcDurationMs": 5.203000000000001,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 467.209,
"heapDeltaBytes": -3617692,
"heapUsedBytes": 77240068,
"domNodes": -262,
"jsHeapTotalBytes": 3997696,
"scriptDurationMs": 12.555999999999997,
"eventListeners": -149,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "subgraph-dom-widget-clipping",
"durationMs": 532.1799999999826,
"styleRecalcs": 47,
"styleRecalcDurationMs": 9.177,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 324.75399999999996,
"heapDeltaBytes": 10983100,
"heapUsedBytes": 71714084,
"domNodes": 20,
"jsHeapTotalBytes": 5242880,
"scriptDurationMs": 99.01499999999999,
"eventListeners": 6,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666682,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "subgraph-dom-widget-clipping",
"durationMs": 514.3419999999423,
"styleRecalcs": 47,
"styleRecalcDurationMs": 8.506,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 303.399,
"heapDeltaBytes": 11811152,
"heapUsedBytes": 72666052,
"domNodes": 20,
"jsHeapTotalBytes": 4718592,
"scriptDurationMs": 92.93900000000001,
"eventListeners": 8,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.663333333333338,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "subgraph-idle",
"durationMs": 1991.5100000000052,
"styleRecalcs": 10,
"styleRecalcDurationMs": 6.5889999999999995,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 384.074,
"heapDeltaBytes": 546296,
"heapUsedBytes": 61710440,
"domNodes": -280,
"jsHeapTotalBytes": 4448256,
"scriptDurationMs": 5.1880000000000015,
"eventListeners": -151,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333332,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "subgraph-idle",
"durationMs": 2009.5230000000015,
"styleRecalcs": 11,
"styleRecalcDurationMs": 6.188999999999998,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 385.014,
"heapDeltaBytes": 385700,
"heapUsedBytes": 61130844,
"domNodes": -281,
"jsHeapTotalBytes": 4710400,
"scriptDurationMs": 5.232999999999998,
"eventListeners": -151,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333335,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "subgraph-mouse-sweep",
"durationMs": 1691.2319999999568,
"styleRecalcs": 75,
"styleRecalcDurationMs": 28.79,
"layouts": 16,
"layoutDurationMs": 3.680999999999999,
"taskDurationMs": 633.664,
"heapDeltaBytes": -2642880,
"heapUsedBytes": 57966392,
"domNodes": -279,
"jsHeapTotalBytes": 5758976,
"scriptDurationMs": 68.34400000000001,
"eventListeners": -151,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "subgraph-mouse-sweep",
"durationMs": 1694.8359999999525,
"styleRecalcs": 75,
"styleRecalcDurationMs": 25.364,
"layouts": 16,
"layoutDurationMs": 3.1380000000000003,
"taskDurationMs": 616.7560000000001,
"heapDeltaBytes": 19116748,
"heapUsedBytes": 80174104,
"domNodes": -278,
"jsHeapTotalBytes": 4448256,
"scriptDurationMs": 63.26699999999999,
"eventListeners": -149,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "subgraph-transition-enter",
"durationMs": 866.9399999999996,
"styleRecalcs": 20,
"styleRecalcDurationMs": 24.290999999999993,
"layouts": 15,
"layoutDurationMs": 10.663999999999998,
"taskDurationMs": 702.2870000000001,
"heapDeltaBytes": -467036,
"heapUsedBytes": 96523592,
"domNodes": 13673,
"jsHeapTotalBytes": 10485760,
"scriptDurationMs": 11.275999999999994,
"eventListeners": 2373,
"totalBlockingTimeMs": 80,
"frameDurationMs": 16.66333333333332,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "viewport-pan-sweep",
"durationMs": 8183.670000000006,
"styleRecalcs": 251,
"styleRecalcDurationMs": 42.098,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 3236.084,
"heapDeltaBytes": 7493160,
"heapUsedBytes": 81587856,
"domNodes": -256,
"jsHeapTotalBytes": -827392,
"scriptDurationMs": 801.0690000000001,
"eventListeners": -133,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.670000000000012,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "viewport-pan-sweep",
"durationMs": 8086.802000000034,
"styleRecalcs": 249,
"styleRecalcDurationMs": 34.61,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 3136.108,
"heapDeltaBytes": 9512780,
"heapUsedBytes": 83935660,
"domNodes": -264,
"jsHeapTotalBytes": -1351680,
"scriptDurationMs": 801.055,
"eventListeners": -133,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333332,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "vue-large-graph-idle",
"durationMs": 14678.184999999985,
"styleRecalcs": 0,
"styleRecalcDurationMs": 0,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 13970.756,
"heapDeltaBytes": -53534856,
"heapUsedBytes": 178967660,
"domNodes": -8312,
"jsHeapTotalBytes": -14573568,
"scriptDurationMs": 91.63300000000001,
"eventListeners": -16389,
"totalBlockingTimeMs": 0,
"frameDurationMs": 17.780000000000047,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "vue-large-graph-idle",
"durationMs": 14681.015000000003,
"styleRecalcs": 0,
"styleRecalcDurationMs": 0,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 13945.519,
"heapDeltaBytes": -31871468,
"heapUsedBytes": 175652228,
"domNodes": -8312,
"jsHeapTotalBytes": -16191488,
"scriptDurationMs": 87.86299999999999,
"eventListeners": -16391,
"totalBlockingTimeMs": 0,
"frameDurationMs": 17.223333333333358,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "vue-large-graph-pan",
"durationMs": 17456.657000000007,
"styleRecalcs": 156,
"styleRecalcDurationMs": 15.86499999999999,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 16930.715,
"heapDeltaBytes": -24188500,
"heapUsedBytes": 196223224,
"domNodes": -8312,
"jsHeapTotalBytes": -10231808,
"scriptDurationMs": 342.508,
"eventListeners": -16385,
"totalBlockingTimeMs": 9,
"frameDurationMs": 17.776666666666642,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "vue-large-graph-pan",
"durationMs": 17734.118000000082,
"styleRecalcs": 154,
"styleRecalcDurationMs": 15.54899999999998,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 17171.287,
"heapDeltaBytes": -28409784,
"heapUsedBytes": 190676732,
"domNodes": -8312,
"jsHeapTotalBytes": -12066816,
"scriptDurationMs": 348.55400000000003,
"eventListeners": -16387,
"totalBlockingTimeMs": 28,
"frameDurationMs": 17.776666666666642,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "workflow-execution",
"durationMs": 84.87400000001344,
"styleRecalcs": 6,
"styleRecalcDurationMs": 10.375999999999998,
"layouts": 2,
"layoutDurationMs": 0.39100000000000007,
"taskDurationMs": 59.198,
"heapDeltaBytes": 3156728,
"heapUsedBytes": 63567748,
"domNodes": 109,
"jsHeapTotalBytes": 524288,
"scriptDurationMs": 4.621,
"eventListeners": 49,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.663333333333338,
"p95FrameDurationMs": 16.700000000000273
},
{
"name": "workflow-execution",
"durationMs": 114.59500000000844,
"styleRecalcs": 8,
"styleRecalcDurationMs": 15.754000000000001,
"layouts": 2,
"layoutDurationMs": 5.841000000000001,
"taskDurationMs": 86.15700000000001,
"heapDeltaBytes": 3035444,
"heapUsedBytes": 63429220,
"domNodes": 126,
"jsHeapTotalBytes": 0,
"scriptDurationMs": 4.7010000000000005,
"eventListeners": 25,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000273
}
]
}🎭 Playwright: ✅ 1829 passed, 0 failed · 3 flaky📊 Browser Reports
|
🎭 Playwright: ⏳ Running... |
Codecov Report❌ Patch coverage is
@@ Coverage Diff @@
## dante/fe-1584-guard-prompt-workspace #15431 +/- ##
=======================================================================
Coverage ? 80.98%
=======================================================================
Files ? 1883
Lines ? 123036
Branches ? 37873
=======================================================================
Hits ? 99640
Misses ? 22895
Partials ? 501
Flags with carried forward coverage won't be shown. Click here to find out more.
🚀 New features to boost your workflow:
|
7ab4c1a to
6d0ad50
Compare
ec5211b to
679dd13
Compare
Amp-Thread-ID: https://ampcode.com/threads/T-01a01a57-6259-75bd-bb52-c149e0f7bb9c Co-authored-by: Amp <amp@ampcode.com>
6d0ad50 to
398664a
Compare
679dd13 to
92cd477
Compare
Amp-Thread-ID: https://ampcode.com/threads/T-01a02a4c-0f97-734f-999f-3f04a1e74795 Co-authored-by: Amp <amp@ampcode.com>
christian-byrne
left a comment
There was a problem hiding this comment.
This is careful work. Snapshotting workspace ID plus transition generation and revalidating between the status and balance calls is the right fix for the TOCTOU window, and splitting the Promise.allSettled pair so the second call cannot land after a switch is a detail that is easy to miss. The A→B→A coverage is good.
One thing I think is a real problem, in pollOperation:
if (operation.workspaceId !== workspaceStore.activeWorkspaceId) {
scheduleNextPoll(opId)
return
}
if (stopIfTimedOut(opId, operation)) returnMoving the ownership check ahead of the timeout check is right for the UI goal, since A's failure should not surface in B. But it also means stopIfTimedOut is unreachable for as long as the operation's workspace is inactive, and the same reordering is in the catch branch.
scheduleNextPoll has exponential backoff capped at MAX_INTERVAL_MS and no attempt cap, and the only terminal conditions are the operation being removed, its status leaving pending, or stopIfTimedOut firing. So an operation started in A, where the user switches to B and stays there, polls forever at the max interval and never reaches a terminal state. It holds a setTimeout chain and a map entry for the rest of the session.
I think the intent is to defer presenting the timeout, not to defer evaluating it. Could the inactive path still evaluate the timeout and resolve terminal silently, and only suppress the failure UI? Something like marking it timed out without the toast, so a user who never returns to A does not leave a poll loop running.
Worth a test for the case where the user switches away and never returns, since the current suite covers switching away and coming back.
Why
Local/Desktop can switch billing workspaces without reloading. Billing requests and post-success reconciliation are asynchronous, so a completion originating in Workspace A must never refresh state or show success/failure UI in Workspace B.
Root cause
The original guard checked workspace ownership only before and after
useBillingContext()reconciliation. That context dynamically selects the active adapter between awaits, leaving a TOCTOU window where A's completion could refresh B. The synchronous top-up and Team subscription paths also handled successful A responses against whichever workspace was active when the response returned. Timeout handling ran before ownership checks, so an inactive A operation could show failure UI in B.Change
AS IS / TO BE
AS IS: Workspace A completion could refresh or present success/failure state in Workspace B when the user switched during an awaited request.
TO BE: Each completion remains scoped to its originating workspace identity; stale responses are ignored and cannot mutate or present UI in another workspace.
No visual layout changes are introduced, so screenshots would not demonstrate these timing-only differences. Delayed-response tests capture the before/after behavior.
Test plan
pnpm test:unit src/platform/workspace/stores/billingOperationStore.test.ts— 72 passedpnpm test:unit src/platform/workspace/components/TopUpCreditsDialogContentWorkspace.test.ts— 20 passedpnpm test:unit src/platform/workspace/composables/useSubscriptionCheckout.test.ts— 103 passedpnpm test:unit src/composables/billing/useBillingContext.test.ts— 41 passedpnpm typecheckpnpm knip --cacheStack