[backport cloud/1.44] fix(cloud): stop bouncing working users to /cloud/survey mid-session#12320
Conversation
…12301) ## Summary `getSurveyCompletedStatus` (auth.ts) now resolves ambiguous responses to "completed" instead of "not completed", so transient backend errors no longer bounce working users to `/cloud/survey`. | Backend response | Old behavior | New behavior | |---|---|---| | 200 with non-empty `value` | `true` (completed) | `true` (completed) | | 200 with empty `value` | `false` (not completed) | `false` (not completed) | | 404 | `false` → bounced | `true` (treat as completed) | | 5xx | `false` → bounced | `true` (treat as completed) | | 401 / 403 | `false` → bounced | `true` (treat as completed) | | Network error | `false` → bounced | `true` (treat as completed) | Only a definitive `200` with empty `value` is treated as "not completed". Everything else fails open. The dedicated auth layer handles re-authentication on the next API call, so 401/403 doesn't need a separate branch here. ## Why User reports from team-plan customers: _"I was working in a workflow, hit run, and then got logged out and redirected to a survey screen."_ Datadog shows ~7,000 distinct users/day hitting the `setting key onboarding_survey not found` path on prod ingest. With `onboarding_survey_enabled: true` in prod dynamic config and the catch-all `!response.ok` returning `false`, any mid-session reload tripped a redirect to `/cloud/survey`. User-validated requirement: rather miss showing the survey to a few users than show it duplicately or interrupt working customers. ## Trade-off worth product review A genuinely brand-new user whose `User.Settings` JSON is empty also returns 404 from `/api/settings/onboarding_survey` — the backend doesn't distinguish "key absent for existing user" from "user has no settings yet". With this change, that 404 is treated as "completed", so the survey gate does not fire on the strict 404 path. New users will still see the survey if signup pre-populates the `onboarding_survey` key with an empty object (`200` with empty `value`); if not, the survey is missed on initial signup. We picked this trade-off per the product call that false positives (bouncing paying customers) are strictly worse than false negatives (occasionally missing a new user). The clean fix to recover the new-user signal is a backend change: return `200` with `value: null` when the `User` row exists but the key is absent — distinguishing "no survey saved" from "user not found". Out of scope for this PR; filing as follow-up if accepted. ## Test plan - [ ] Logged-in user with completed survey navigates around — no redirect - [ ] Logged-in user with no survey, fresh tab — redirected to `/cloud/survey` (gate still works for new sessions) - [ ] Logged-in user with no survey, after submitting — no redirect on next nav - [ ] Simulate transient 5xx on `/api/settings/onboarding_survey` (DevTools blocking) — user stays on current page, no redirect Unit coverage in `auth.test.ts` locks the resolution table above against drift (one test per branch, 8 total). ## Companion PRs None — frontend only. ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-12301-fix-cloud-stop-bouncing-working-users-to-cloud-survey-mid-session-3616d73d365081128ba7e266ad7ccff9) by [Unito](https://www.unito.io) --------- Co-authored-by: GitHub Action <action@github.com> Co-authored-by: Alexander Brown <drjkl@comfy.org>
🎨 Storybook: ✅ Built — View Storybook |
🎭 Playwright: ✅ 1538 passed, 0 failed · 3 flaky📊 Browser Reports
|
Codecov Report✅ All modified and coverable lines are covered by tests. @@ Coverage Diff @@
## cloud/1.44 #12320 +/- ##
=============================================
Coverage ? 58.42%
=============================================
Files ? 1407
Lines ? 71556
Branches ? 18930
=============================================
Hits ? 41805
Misses ? 29247
Partials ? 504
Flags with carried forward coverage won't be shown. Click here to find out more.
🚀 New features to boost your workflow:
|
📦 Bundle Size
⚡ Performance Report
No baseline found — showing absolute values.
Raw data{
"timestamp": "2026-05-19T00:02:59.687Z",
"gitSha": "7f0a6edbbf2959569156271eeab704f6de3e8141",
"branch": "backport-12301-to-cloud-1.44",
"measurements": [
{
"name": "canvas-idle",
"durationMs": 2020.3710000000115,
"styleRecalcs": 9,
"styleRecalcDurationMs": 10.173000000000002,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 460.079,
"heapDeltaBytes": -1038540,
"heapUsedBytes": 49254840,
"domNodes": 18,
"jsHeapTotalBytes": 17039360,
"scriptDurationMs": 26.334999999999997,
"eventListeners": 4,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "canvas-idle",
"durationMs": 2071.327999999994,
"styleRecalcs": 10,
"styleRecalcDurationMs": 10.201000000000002,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 547.967,
"heapDeltaBytes": -303548,
"heapUsedBytes": 68257188,
"domNodes": -261,
"jsHeapTotalBytes": 21192704,
"scriptDurationMs": 35.077,
"eventListeners": -129,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "canvas-mouse-sweep",
"durationMs": 2057.9119999999875,
"styleRecalcs": 78,
"styleRecalcDurationMs": 51.41499999999999,
"layouts": 12,
"layoutDurationMs": 4.0089999999999995,
"taskDurationMs": 1082.986,
"heapDeltaBytes": 13159976,
"heapUsedBytes": 62936468,
"domNodes": -256,
"jsHeapTotalBytes": 19881984,
"scriptDurationMs": 151.492,
"eventListeners": -133,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "canvas-mouse-sweep",
"durationMs": 1911.7239999999356,
"styleRecalcs": 74,
"styleRecalcDurationMs": 43.111,
"layouts": 12,
"layoutDurationMs": 4.252,
"taskDurationMs": 924.397,
"heapDeltaBytes": 19996088,
"heapUsedBytes": 68934100,
"domNodes": -265,
"jsHeapTotalBytes": 17690624,
"scriptDurationMs": 134.08299999999997,
"eventListeners": -129,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "canvas-zoom-sweep",
"durationMs": 1739.590000000021,
"styleRecalcs": 32,
"styleRecalcDurationMs": 20.176,
"layouts": 6,
"layoutDurationMs": 0.722,
"taskDurationMs": 360.673,
"heapDeltaBytes": 405992,
"heapUsedBytes": 49086112,
"domNodes": 76,
"jsHeapTotalBytes": 14942208,
"scriptDurationMs": 26.995,
"eventListeners": 19,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66999999999998,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "canvas-zoom-sweep",
"durationMs": 1726.3110000000097,
"styleRecalcs": 33,
"styleRecalcDurationMs": 20.598,
"layouts": 6,
"layoutDurationMs": 0.7900000000000001,
"taskDurationMs": 394.78099999999995,
"heapDeltaBytes": 843884,
"heapUsedBytes": 50189984,
"domNodes": 79,
"jsHeapTotalBytes": 14942208,
"scriptDurationMs": 34.842,
"eventListeners": 19,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "dom-widget-clipping",
"durationMs": 683.8730000000623,
"styleRecalcs": 12,
"styleRecalcDurationMs": 9.692000000000002,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 424.573,
"heapDeltaBytes": 7258164,
"heapUsedBytes": 60090736,
"domNodes": 20,
"jsHeapTotalBytes": 15204352,
"scriptDurationMs": 72.24999999999999,
"eventListeners": 2,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "dom-widget-clipping",
"durationMs": 610.2969999999459,
"styleRecalcs": 13,
"styleRecalcDurationMs": 10.003,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 387.486,
"heapDeltaBytes": -12790800,
"heapUsedBytes": 53101772,
"domNodes": 22,
"jsHeapTotalBytes": 21065728,
"scriptDurationMs": 66.473,
"eventListeners": 2,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.799999999999727
},
{
"name": "large-graph-idle",
"durationMs": 2093.1130000000167,
"styleRecalcs": 9,
"styleRecalcDurationMs": 9.988,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 722.9559999999999,
"heapDeltaBytes": 46813548,
"heapUsedBytes": 104011344,
"domNodes": -263,
"jsHeapTotalBytes": 33583104,
"scriptDurationMs": 128.915,
"eventListeners": -129,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "large-graph-idle",
"durationMs": 2027.924999999982,
"styleRecalcs": 9,
"styleRecalcDurationMs": 9.169,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 631.31,
"heapDeltaBytes": 2253840,
"heapUsedBytes": 61033536,
"domNodes": -265,
"jsHeapTotalBytes": 5271552,
"scriptDurationMs": 113.95300000000002,
"eventListeners": -129,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "large-graph-pan",
"durationMs": 2215.8880000000636,
"styleRecalcs": 69,
"styleRecalcDurationMs": 21.843999999999998,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 1311.0059999999999,
"heapDeltaBytes": 3551972,
"heapUsedBytes": 62921412,
"domNodes": -264,
"jsHeapTotalBytes": 1282048,
"scriptDurationMs": 446.624,
"eventListeners": -127,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333332,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "large-graph-pan",
"durationMs": 2296.595000000025,
"styleRecalcs": 67,
"styleRecalcDurationMs": 17.957,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 1398.422,
"heapDeltaBytes": 50336268,
"heapUsedBytes": 110390584,
"domNodes": -267,
"jsHeapTotalBytes": 41652224,
"scriptDurationMs": 470.339,
"eventListeners": -127,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "large-graph-zoom",
"durationMs": 3414.912999999956,
"styleRecalcs": 64,
"styleRecalcDurationMs": 20.711,
"layouts": 60,
"layoutDurationMs": 8.492,
"taskDurationMs": 1620.4140000000002,
"heapDeltaBytes": 42336284,
"heapUsedBytes": 104536620,
"domNodes": -268,
"jsHeapTotalBytes": 34426880,
"scriptDurationMs": 549.548,
"eventListeners": -127,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "large-graph-zoom",
"durationMs": 3460.7300000000123,
"styleRecalcs": 65,
"styleRecalcDurationMs": 21.256000000000004,
"layouts": 60,
"layoutDurationMs": 8.798,
"taskDurationMs": 1622.773,
"heapDeltaBytes": 37939012,
"heapUsedBytes": 99656668,
"domNodes": -266,
"jsHeapTotalBytes": 41447424,
"scriptDurationMs": 565.5490000000001,
"eventListeners": -125,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333332,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "minimap-idle",
"durationMs": 2038.7470000000576,
"styleRecalcs": 9,
"styleRecalcDurationMs": 9.330000000000002,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 629.7080000000001,
"heapDeltaBytes": 14099956,
"heapUsedBytes": 75613404,
"domNodes": -265,
"jsHeapTotalBytes": 4018176,
"scriptDurationMs": 111.25999999999999,
"eventListeners": -131,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333335,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "minimap-idle",
"durationMs": 2061.602999999991,
"styleRecalcs": 7,
"styleRecalcDurationMs": 7.261999999999998,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 734.939,
"heapDeltaBytes": 38817380,
"heapUsedBytes": 100997840,
"domNodes": -265,
"jsHeapTotalBytes": 26038272,
"scriptDurationMs": 118.94500000000001,
"eventListeners": -129,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "subgraph-dom-widget-clipping",
"durationMs": 630.0169999999525,
"styleRecalcs": 46,
"styleRecalcDurationMs": 12.101,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 402.771,
"heapDeltaBytes": 9158388,
"heapUsedBytes": 57853960,
"domNodes": 17,
"jsHeapTotalBytes": 15466496,
"scriptDurationMs": 127.87199999999999,
"eventListeners": 6,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000273
},
{
"name": "subgraph-dom-widget-clipping",
"durationMs": 661.6440000000239,
"styleRecalcs": 45,
"styleRecalcDurationMs": 11.806000000000001,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 416.9700000000001,
"heapDeltaBytes": 16576144,
"heapUsedBytes": 67235900,
"domNodes": 16,
"jsHeapTotalBytes": 18874368,
"scriptDurationMs": 129.414,
"eventListeners": 8,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333332,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "subgraph-idle",
"durationMs": 2031.9899999999507,
"styleRecalcs": 3,
"styleRecalcDurationMs": 2.997,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 517.261,
"heapDeltaBytes": 19922012,
"heapUsedBytes": 68762012,
"domNodes": -264,
"jsHeapTotalBytes": 17952768,
"scriptDurationMs": 22.984000000000005,
"eventListeners": -131,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "subgraph-idle",
"durationMs": 2000.7460000000492,
"styleRecalcs": 8,
"styleRecalcDurationMs": 8.741999999999999,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 423.643,
"heapDeltaBytes": 22958772,
"heapUsedBytes": 71901100,
"domNodes": 16,
"jsHeapTotalBytes": 15204352,
"scriptDurationMs": 21.884,
"eventListeners": 4,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66999999999998,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "subgraph-mouse-sweep",
"durationMs": 1739.7730000000138,
"styleRecalcs": 76,
"styleRecalcDurationMs": 41.45400000000001,
"layouts": 16,
"layoutDurationMs": 4.531999999999999,
"taskDurationMs": 847.476,
"heapDeltaBytes": 15391912,
"heapUsedBytes": 64279208,
"domNodes": -260,
"jsHeapTotalBytes": 19263488,
"scriptDurationMs": 106.896,
"eventListeners": -129,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66999999999998,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "subgraph-mouse-sweep",
"durationMs": 2014.3550000000232,
"styleRecalcs": 88,
"styleRecalcDurationMs": 53.836999999999996,
"layouts": 16,
"layoutDurationMs": 4.9910000000000005,
"taskDurationMs": 1081.209,
"heapDeltaBytes": -4286736,
"heapUsedBytes": 44222268,
"domNodes": -264,
"jsHeapTotalBytes": 14544896,
"scriptDurationMs": 105.29100000000001,
"eventListeners": -131,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "subgraph-transition-enter",
"durationMs": 949.0019999999504,
"styleRecalcs": 17,
"styleRecalcDurationMs": 26.292999999999996,
"layouts": 5,
"layoutDurationMs": 10.720999999999998,
"taskDurationMs": 735.2860000000001,
"heapDeltaBytes": 6918044,
"heapUsedBytes": 96305364,
"domNodes": 13433,
"jsHeapTotalBytes": 14942208,
"scriptDurationMs": 31.57700000000001,
"eventListeners": 2527,
"totalBlockingTimeMs": 136,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "viewport-pan-sweep",
"durationMs": 8547.53100000005,
"styleRecalcs": 249,
"styleRecalcDurationMs": 61.527,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 4768.246999999999,
"heapDeltaBytes": 90200864,
"heapUsedBytes": 148488296,
"domNodes": -263,
"jsHeapTotalBytes": 72060928,
"scriptDurationMs": 1446.9139999999998,
"eventListeners": -155,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.80000000000109
},
{
"name": "viewport-pan-sweep",
"durationMs": 8174.635999999964,
"styleRecalcs": 249,
"styleRecalcDurationMs": 58.991,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 4542.9259999999995,
"heapDeltaBytes": 18778580,
"heapUsedBytes": 77370824,
"domNodes": -264,
"jsHeapTotalBytes": 5214208,
"scriptDurationMs": 1526.2479999999998,
"eventListeners": -113,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "vue-large-graph-idle",
"durationMs": 12487.934999999994,
"styleRecalcs": 0,
"styleRecalcDurationMs": 0,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 12475.345000000001,
"heapDeltaBytes": -43716220,
"heapUsedBytes": 170451992,
"domNodes": -8331,
"jsHeapTotalBytes": 23916544,
"scriptDurationMs": 672.072,
"eventListeners": -16463,
"totalBlockingTimeMs": 0,
"frameDurationMs": 17.220000000000073,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "vue-large-graph-idle",
"durationMs": 12641.66899999998,
"styleRecalcs": 0,
"styleRecalcDurationMs": 0,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 12624.042999999998,
"heapDeltaBytes": -41933356,
"heapUsedBytes": 162754076,
"domNodes": -8331,
"jsHeapTotalBytes": 13168640,
"scriptDurationMs": 677.586,
"eventListeners": -16464,
"totalBlockingTimeMs": 0,
"frameDurationMs": 17.223333333333358,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "vue-large-graph-pan",
"durationMs": 17972.733000000062,
"styleRecalcs": 113,
"styleRecalcDurationMs": 26.637999999999995,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 17947.193,
"heapDeltaBytes": -2272976,
"heapUsedBytes": 259302576,
"domNodes": -8329,
"jsHeapTotalBytes": 5042176,
"scriptDurationMs": 1214.839,
"eventListeners": -16488,
"totalBlockingTimeMs": 14,
"frameDurationMs": 17.776666666666642,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "vue-large-graph-pan",
"durationMs": 14652.957000000015,
"styleRecalcs": 68,
"styleRecalcDurationMs": 21.63100000000001,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 14631.098999999998,
"heapDeltaBytes": -18537932,
"heapUsedBytes": 177584620,
"domNodes": -8335,
"jsHeapTotalBytes": -13131776,
"scriptDurationMs": 971.6440000000001,
"eventListeners": -16460,
"totalBlockingTimeMs": 0,
"frameDurationMs": 17.776666666666642,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "workflow-execution",
"durationMs": 489.09199999991415,
"styleRecalcs": 16,
"styleRecalcDurationMs": 24.382,
"layouts": 5,
"layoutDurationMs": 1.641,
"taskDurationMs": 155.94599999999997,
"heapDeltaBytes": -14772508,
"heapUsedBytes": 51955324,
"domNodes": 168,
"jsHeapTotalBytes": 6123520,
"scriptDurationMs": 31.675,
"eventListeners": 71,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.799999999999727
},
{
"name": "workflow-execution",
"durationMs": 482.2030000000268,
"styleRecalcs": 18,
"styleRecalcDurationMs": 27.665,
"layouts": 5,
"layoutDurationMs": 1.491,
"taskDurationMs": 151.14600000000004,
"heapDeltaBytes": -15047948,
"heapUsedBytes": 51789628,
"domNodes": 159,
"jsHeapTotalBytes": 6385664,
"scriptDurationMs": 28.488,
"eventListeners": 69,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000273
}
]
} |
Backport of #12301 to
cloud/1.44Automatically created by backport workflow.
┆Issue is synchronized with this Notion page by Unito