refactor(billing): retire consolidated billing rollout flag - #14615
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 |
📄 Knowledge reviewDosu skipped reviewing this PR because your organization has used its |
🎭 Playwright: ✅ 1782 passed, 0 failed · 6 flaky📊 Browser Reports
🎨 Storybook: ✅ Built — View Storybook📦 Bundle Size
⚡ Performance Report
Absolute values
Raw data{
"timestamp": "2026-08-09T00:09:02.488Z",
"gitSha": "028840078a9aac2cdb9aecb409db2cebd2a0b466",
"branch": "dante/retire-consolidated-billing-flag",
"measurements": [
{
"name": "canvas-idle",
"durationMs": 2104.7019999999748,
"styleRecalcs": 8,
"styleRecalcDurationMs": 5.418999999999999,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 604.8979999999999,
"heapDeltaBytes": 5885732,
"heapUsedBytes": 70148608,
"domNodes": 16,
"jsHeapTotalBytes": 24903680,
"scriptDurationMs": 17.870000000000005,
"eventListeners": 4,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "canvas-idle",
"durationMs": 1996.2050000000318,
"styleRecalcs": 11,
"styleRecalcDurationMs": 7.020999999999999,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 489.866,
"heapDeltaBytes": 5624452,
"heapUsedBytes": 69859468,
"domNodes": 22,
"jsHeapTotalBytes": 25427968,
"scriptDurationMs": 21.381000000000004,
"eventListeners": 6,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "canvas-mouse-sweep",
"durationMs": 1781.6120000000524,
"styleRecalcs": 74,
"styleRecalcDurationMs": 31.064,
"layouts": 12,
"layoutDurationMs": 3.398,
"taskDurationMs": 773.2139999999999,
"heapDeltaBytes": -129728,
"heapUsedBytes": 64314348,
"domNodes": 56,
"jsHeapTotalBytes": 25165824,
"scriptDurationMs": 101.992,
"eventListeners": 4,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "canvas-mouse-sweep",
"durationMs": 1873.091000000045,
"styleRecalcs": 78,
"styleRecalcDurationMs": 35.912,
"layouts": 12,
"layoutDurationMs": 3.574,
"taskDurationMs": 893.254,
"heapDeltaBytes": -12135316,
"heapUsedBytes": 52220108,
"domNodes": -280,
"jsHeapTotalBytes": 24092672,
"scriptDurationMs": 107.42599999999999,
"eventListeners": -153,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "canvas-zoom-sweep",
"durationMs": 1660.587000000021,
"styleRecalcs": 31,
"styleRecalcDurationMs": 14.096,
"layouts": 6,
"layoutDurationMs": 0.605,
"taskDurationMs": 356.70799999999997,
"heapDeltaBytes": 8494168,
"heapUsedBytes": 72536652,
"domNodes": 81,
"jsHeapTotalBytes": 24903680,
"scriptDurationMs": 18.724,
"eventListeners": 21,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333335,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "canvas-zoom-sweep",
"durationMs": 1723.3929999999873,
"styleRecalcs": 32,
"styleRecalcDurationMs": 14.801,
"layouts": 6,
"layoutDurationMs": 0.6199999999999999,
"taskDurationMs": 369.78599999999994,
"heapDeltaBytes": 8824120,
"heapUsedBytes": 73158320,
"domNodes": 78,
"jsHeapTotalBytes": 24903680,
"scriptDurationMs": 21.801999999999996,
"eventListeners": 19,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "dom-widget-clipping",
"durationMs": 595.9920000000238,
"styleRecalcs": 13,
"styleRecalcDurationMs": 13.011,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 369.82599999999996,
"heapDeltaBytes": -11310476,
"heapUsedBytes": 53080068,
"domNodes": 22,
"jsHeapTotalBytes": 24903680,
"scriptDurationMs": 53.17100000000001,
"eventListeners": 0,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666682,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "dom-widget-clipping",
"durationMs": 561.8469999999434,
"styleRecalcs": 13,
"styleRecalcDurationMs": 7.697999999999999,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 351.453,
"heapDeltaBytes": -11219668,
"heapUsedBytes": 53166472,
"domNodes": 22,
"jsHeapTotalBytes": 25165824,
"scriptDurationMs": 49.739,
"eventListeners": 0,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666682,
"p95FrameDurationMs": 16.799999999999727
},
{
"name": "large-graph-idle",
"durationMs": 2039.0810000000101,
"styleRecalcs": 9,
"styleRecalcDurationMs": 6.856999999999998,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 642.4380000000001,
"heapDeltaBytes": 6777196,
"heapUsedBytes": 66267956,
"domNodes": -282,
"jsHeapTotalBytes": 2461696,
"scriptDurationMs": 93.691,
"eventListeners": -149,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "large-graph-idle",
"durationMs": 2024.0009999999984,
"styleRecalcs": 10,
"styleRecalcDurationMs": 7.6320000000000014,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 657.573,
"heapDeltaBytes": 3273540,
"heapUsedBytes": 64935756,
"domNodes": -279,
"jsHeapTotalBytes": 3510272,
"scriptDurationMs": 93.841,
"eventListeners": -179,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333332,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "large-graph-pan",
"durationMs": 2148.4570000000076,
"styleRecalcs": 68,
"styleRecalcDurationMs": 12.773,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 1195.5770000000002,
"heapDeltaBytes": -4971916,
"heapUsedBytes": 55548128,
"domNodes": -284,
"jsHeapTotalBytes": 5345280,
"scriptDurationMs": 380.094,
"eventListeners": -179,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333332,
"p95FrameDurationMs": 16.699999999999818
},
{
"name": "large-graph-pan",
"durationMs": 2162.742000000094,
"styleRecalcs": 70,
"styleRecalcDurationMs": 14.103000000000002,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 1241.1209999999999,
"heapDeltaBytes": 2550416,
"heapUsedBytes": 63295936,
"domNodes": -282,
"jsHeapTotalBytes": 4751360,
"scriptDurationMs": 417.491,
"eventListeners": -147,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "large-graph-zoom",
"durationMs": 3128.7269999999694,
"styleRecalcs": 65,
"styleRecalcDurationMs": 13.652000000000005,
"layouts": 60,
"layoutDurationMs": 7.222000000000001,
"taskDurationMs": 1408.133,
"heapDeltaBytes": -6516024,
"heapUsedBytes": 55629348,
"domNodes": -289,
"jsHeapTotalBytes": 6131712,
"scriptDurationMs": 463.6580000000001,
"eventListeners": -183,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333335,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "large-graph-zoom",
"durationMs": 3164.675999999986,
"styleRecalcs": 65,
"styleRecalcDurationMs": 14.019999999999998,
"layouts": 60,
"layoutDurationMs": 7.365,
"taskDurationMs": 1413.182,
"heapDeltaBytes": -5719476,
"heapUsedBytes": 56523528,
"domNodes": -287,
"jsHeapTotalBytes": 6656000,
"scriptDurationMs": 470.014,
"eventListeners": -153,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "minimap-idle",
"durationMs": 2039.32199999997,
"styleRecalcs": 9,
"styleRecalcDurationMs": 6.780000000000001,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 649.193,
"heapDeltaBytes": 7587864,
"heapUsedBytes": 68171456,
"domNodes": -284,
"jsHeapTotalBytes": 3510272,
"scriptDurationMs": 91.034,
"eventListeners": -179,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333335,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "minimap-idle",
"durationMs": 2022.9689999999891,
"styleRecalcs": 9,
"styleRecalcDurationMs": 6.9479999999999995,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 636.787,
"heapDeltaBytes": 7010784,
"heapUsedBytes": 67790080,
"domNodes": -281,
"jsHeapTotalBytes": 3510272,
"scriptDurationMs": 92.90599999999999,
"eventListeners": -149,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333332,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "subgraph-dom-widget-clipping",
"durationMs": 555.0240000000031,
"styleRecalcs": 48,
"styleRecalcDurationMs": 10.311999999999998,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 346.47,
"heapDeltaBytes": -10818240,
"heapUsedBytes": 53480092,
"domNodes": 22,
"jsHeapTotalBytes": 25952256,
"scriptDurationMs": 117.663,
"eventListeners": 8,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.663333333333338,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "subgraph-dom-widget-clipping",
"durationMs": 567.9909999998927,
"styleRecalcs": 47,
"styleRecalcDurationMs": 9.011,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 380.808,
"heapDeltaBytes": -10860296,
"heapUsedBytes": 53883904,
"domNodes": 20,
"jsHeapTotalBytes": 26476544,
"scriptDurationMs": 112.15100000000001,
"eventListeners": 6,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666682,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "subgraph-idle",
"durationMs": 2003.358999999989,
"styleRecalcs": 10,
"styleRecalcDurationMs": 6.362999999999999,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 435.588,
"heapDeltaBytes": 5758080,
"heapUsedBytes": 70448700,
"domNodes": 20,
"jsHeapTotalBytes": 24641536,
"scriptDurationMs": 17.493000000000002,
"eventListeners": 6,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "subgraph-idle",
"durationMs": 2016.6360000000623,
"styleRecalcs": 10,
"styleRecalcDurationMs": 6.447999999999999,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 484.796,
"heapDeltaBytes": -17014960,
"heapUsedBytes": 47456520,
"domNodes": -280,
"jsHeapTotalBytes": 23306240,
"scriptDurationMs": 16.354,
"eventListeners": -151,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.670000000000012,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "subgraph-mouse-sweep",
"durationMs": 1677.989000000025,
"styleRecalcs": 75,
"styleRecalcDurationMs": 30.250000000000004,
"layouts": 16,
"layoutDurationMs": 3.538,
"taskDurationMs": 709.218,
"heapDeltaBytes": -3278004,
"heapUsedBytes": 61397104,
"domNodes": 63,
"jsHeapTotalBytes": 24117248,
"scriptDurationMs": 75.16099999999999,
"eventListeners": 4,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333332,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "subgraph-mouse-sweep",
"durationMs": 1714.4640000000209,
"styleRecalcs": 77,
"styleRecalcDurationMs": 33.96399999999999,
"layouts": 16,
"layoutDurationMs": 4.553,
"taskDurationMs": 742.9539999999998,
"heapDeltaBytes": -3912768,
"heapUsedBytes": 60720660,
"domNodes": 62,
"jsHeapTotalBytes": 25165824,
"scriptDurationMs": 77.497,
"eventListeners": 4,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333335,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "subgraph-transition-enter",
"durationMs": 1368.0259999999862,
"styleRecalcs": 19,
"styleRecalcDurationMs": 26.530000000000005,
"layouts": 15,
"layoutDurationMs": 10.722,
"taskDurationMs": 851.5070000000001,
"heapDeltaBytes": 32913792,
"heapUsedBytes": 100385156,
"domNodes": 13673,
"jsHeapTotalBytes": 13107200,
"scriptDurationMs": 31.55100000000001,
"eventListeners": 2375,
"totalBlockingTimeMs": 114,
"frameDurationMs": 16.66999999999998,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "viewport-pan-sweep",
"durationMs": 8187.3769999999695,
"styleRecalcs": 249,
"styleRecalcDurationMs": 34.307,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 4179.536,
"heapDeltaBytes": 6395716,
"heapUsedBytes": 68624836,
"domNodes": -283,
"jsHeapTotalBytes": 7634944,
"scriptDurationMs": 1229.827,
"eventListeners": -133,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "viewport-pan-sweep",
"durationMs": 8210.365000000023,
"styleRecalcs": 250,
"styleRecalcDurationMs": 35.901,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 4233.6720000000005,
"heapDeltaBytes": 7344932,
"heapUsedBytes": 67708952,
"domNodes": -280,
"jsHeapTotalBytes": 5275648,
"scriptDurationMs": 1244.385,
"eventListeners": -163,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "vue-large-graph-idle",
"durationMs": 15033.913999999982,
"styleRecalcs": 0,
"styleRecalcDurationMs": 0,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 15006.823,
"heapDeltaBytes": -64260096,
"heapUsedBytes": 154572620,
"domNodes": -8312,
"jsHeapTotalBytes": -10952704,
"scriptDurationMs": 532.912,
"eventListeners": -16389,
"totalBlockingTimeMs": 0,
"frameDurationMs": 17.223333333333358,
"p95FrameDurationMs": 16.80000000000291
},
{
"name": "vue-large-graph-idle",
"durationMs": 15005.231999999978,
"styleRecalcs": 0,
"styleRecalcDurationMs": 0,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 14971.496,
"heapDeltaBytes": -56909056,
"heapUsedBytes": 166785404,
"domNodes": -8312,
"jsHeapTotalBytes": -6754304,
"scriptDurationMs": 530.528,
"eventListeners": -16389,
"totalBlockingTimeMs": 0,
"frameDurationMs": 18.333333333333332,
"p95FrameDurationMs": 16.80000000000291
},
{
"name": "vue-large-graph-pan",
"durationMs": 17808.690000000013,
"styleRecalcs": 122,
"styleRecalcDurationMs": 13.396999999999991,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 17767.393,
"heapDeltaBytes": -39827800,
"heapUsedBytes": 165966896,
"domNodes": -8312,
"jsHeapTotalBytes": -12263424,
"scriptDurationMs": 838.5600000000001,
"eventListeners": -16387,
"totalBlockingTimeMs": 29,
"frameDurationMs": 18.33666666666674,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "vue-large-graph-pan",
"durationMs": 18182.537000000026,
"styleRecalcs": 122,
"styleRecalcDurationMs": 14.833999999999985,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 18144.982,
"heapDeltaBytes": -55914404,
"heapUsedBytes": 167033376,
"domNodes": -8312,
"jsHeapTotalBytes": -9641984,
"scriptDurationMs": 881.478,
"eventListeners": -16381,
"totalBlockingTimeMs": 19,
"frameDurationMs": 17.216666666666665,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "workflow-execution",
"durationMs": 445.0930000000426,
"styleRecalcs": 12,
"styleRecalcDurationMs": 16.942,
"layouts": 3,
"layoutDurationMs": 0.593,
"taskDurationMs": 103.58500000000001,
"heapDeltaBytes": 5312376,
"heapUsedBytes": 68747832,
"domNodes": 134,
"jsHeapTotalBytes": 4980736,
"scriptDurationMs": 9.405,
"eventListeners": 97,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.670000000000012,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "workflow-execution",
"durationMs": 465.09199999991324,
"styleRecalcs": 15,
"styleRecalcDurationMs": 22.586999999999996,
"layouts": 3,
"layoutDurationMs": 1.1010000000000002,
"taskDurationMs": 123.29499999999999,
"heapDeltaBytes": 5092284,
"heapUsedBytes": 68547420,
"domNodes": 125,
"jsHeapTotalBytes": 5767168,
"scriptDurationMs": 9.111,
"eventListeners": 97,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.800000000000182
}
]
} |
Codecov Report✅ All modified and coverable lines are covered by tests. @@ Coverage Diff @@
## dante/canonical-billing-status #14615 +/- ##
=================================================================
Coverage ? 78.73%
=================================================================
Files ? 1866
Lines ? 115401
Branches ? 33540
=================================================================
Hits ? 90860
Misses ? 23998
Partials ? 543
Flags with carried forward coverage won't be shown. Click here to find out more.
🚀 New features to boost your workflow:
|
christian-byrne
left a comment
There was a problem hiding this comment.
+19/-121 pure deletion, and the sweep looks complete: no remaining reader of consolidated_billing_enabled anywhere in src/, browser_tests/ or the storybook mocks. One non-blocking question inline, and it is inherited from the rail predicate rather than created here.
Worth stating explicitly, since this PR is the point of no return for the second flag: I could not verify from this repo that consolidated_billing_enabled is at 100 percent, and after the backend removes the key from common/featuregates/flags.go, reverting this PR does not restore the previous behaviour, because the reinstated read of a deleted key resolves to false, which is the off branch. flags.go carries the BE-119 rule requiring confirmation that no current frontend release reads a flag before it is removed, added after cloud#3171 had to be reverted by #3227/#3228 for exactly this. The backend PR that removes both keys, Comfy-Org/cloud#6040, pins its dependency to #14530 rather than to this stack, so please confirm the ordering and the rollout number before this merges.
Separately, the billing-rail routing is now spread over several sites reading one input (useBillingRouting.ts:32 asks === 'legacy_stripe' while launchCancellationFlow.ts:30 asks !== 'stripe', so metronome classifies differently in each). That is pre-existing and not this PR's job; I will open a follow-up issue for consolidating it and documenting the legacy_stripe exit condition.
|
Follow-up issue filed as promised in the review: #14645 (consolidate the |
|
Addressed the rollout review note in the PR body: it now records the frontend-version/open-tab drain requirement, the revert-is-not-restore constraint, the required 100% |
e87d46a to
2e7cb4f
Compare
c218cfc to
a453ab9
Compare
2e7cb4f to
7f2854f
Compare
55b1360 to
65caf81
Compare
65caf81 to
51e0fdf
Compare
a5b04e8 to
ba7ac38
Compare
51e0fdf to
da0ad43
Compare
christian-byrne
left a comment
There was a problem hiding this comment.
Re-approving at the current head, since my earlier approval predated the review and the branch has moved several times.
The sweep is complete: no remaining reader of consolidated_billing_enabled anywhere in src, browser_tests or the storybook mocks, and the delta since my last look is inherited from the rebase plus fixture updates rather than new logic.
I confirmed offline that consolidated_billing_enabled is at 100 percent, so retiring it does not change behaviour for any live cohort.
The rail predicate is unchanged and I am not re-litigating it. Recording the outcome so the decision is legible later: a personal workspace with an omitted or unrecognised billing_rail routes to workspace billing by design, and the previous server-side recovery lever is gone, so a mistake there needs a frontend rollback. That trade-off is deliberate and was confirmed in thread.
Follow-up for the routing consolidation and the metronome classification split between useBillingRouting and launchCancellationFlow is tracked in #14645, and is not this PR's job.
Note that this stack should not merge ahead of the backend coordination tracked in #14738: Comfy-Org/cloud#6040 still pins its dependency to a competing frontend PR that will never deploy.
## Summary Part 1/4 of the workspace/billing rollout retirement stack. Makes Cloud workspace initialization failures recoverable without changing either rollout flag or flag-off behavior. Predecessor: none (base: main). Successor: [Comfy-Org#14613](Comfy-Org#14613). ## Root cause `WorkspaceAuthGate` failed closed on config, unified-auth, workspace initialization, and timeout failures but exposed no recovery action. The team workspace store also treated `error` as terminal, and the Cloud account popover disappeared while workspace initialization was loading or failed, preventing account actions. ## Changes - Render a fail-closed initialization error with a retry action for config, timeout, unified-auth, and workspace failures. - Permit `teamWorkspaceStore.initialize()` to retry from `error`. - Keep the account menu usable in flag-on loading/error states via account-only workspace popover content. - Preserve legacy popover and gate behavior when `team_workspaces_enabled` is off. - Filter empty logged-out Workspace settings groups. - Add only the Cloud boot session/workspace fixture coverage needed by this layer; both rollout keys remain supported. ## AS IS Initialization failures permanently gate the app and loading/error workspace state removes Cloud account actions. ## TO BE Initialization failures remain fail-closed but can be retried, and Cloud account actions remain available during loading/error. This is the only intended visual change in the stack; it is a state-dependent recovery UI, so no static screenshot is required. ## Regression coverage - WorkspaceAuthGate failure, timeout, and retry paths with asynchronous settling. - Team workspace store `error -> loading -> ready` retry. - Current user button/popover flag-off, flag-on loading/error, and ready behavior. - Logged-out settings group filtering. - Cloud boot fixture session/workspace bootstrap. ## Validation - `pnpm test:unit src/platform/workspace/auth/WorkspaceAuthGate.test.ts src/platform/workspace/stores/teamWorkspaceStore.test.ts src/components/topbar/CurrentUserButton.test.ts src/platform/workspace/components/CurrentUserPopoverWorkspace.test.ts src/platform/settings/composables/useSettingUI.test.ts` (125 passed) - `pnpm typecheck` (passed via commit hook after final fix) - `pnpm typecheck:browser` (passed) - targeted `oxfmt --check` (passed) - `git diff --check public/main...HEAD` (passed) - `pnpm knip --cache` (passed via push hook) ## Review focus Review only recoverability, retry state transitions, and preservation of both rollout modes. Flag retirement belongs to later PRs. ## Stack/deployment order 1. **This PR** — recovery foundation 2. [Comfy-Org#14613](Comfy-Org#14613) — retire team workspace rollout flag 3. [Comfy-Org#14614](Comfy-Org#14614) — canonical billing status and legacy rail preservation 4. [Comfy-Org#14615](Comfy-Org#14615) — retire consolidated billing rollout flag Deploy the frontend stack before the backend removes `/api/features` compatibility keys. --------- Co-authored-by: Dante <6510430+dante01yoon@users.noreply.github.com> Co-authored-by: Amp <amp@ampcode.com> Co-authored-by: Christian Byrne <cbyrne@comfy.org>
ba7ac38 to
b1b9451
Compare
da0ad43 to
ef30c0b
Compare
b1b9451 to
22c2523
Compare
) ## Summary Part 2/4 of the workspace/billing rollout retirement stack. Removes `team_workspaces_enabled` atomically after the recovery foundation in [Comfy-Org#14612](Comfy-Org#14612). Predecessor: [Comfy-Org#14612](Comfy-Org#14612). Successor: [Comfy-Org#14614](Comfy-Org#14614). ## Root cause Workspace mode was split across a long-lived rollout key in session cookies, token selection, auth restoration, workspace boot, account UI/settings, URL actions, and billing routing. Once Cloud workspace support is the default, keeping both branches creates divergent authentication and billing behavior and makes backend feature-key removal unsafe. ## Changes - Remove the team workspace key from remote-config types, enum/getters/cache/refresh, local override, Storybook, browser payloads, and obsolete flag tests. - Make Cloud workspace auth/session restoration/switch/recovery and workspace UI/loaders unconditional after the authenticated config checkpoint. - Preserve OSS and unloaded-workspace legacy bootstrap behavior. - Preserve `consolidated_billing_enabled` and the intermediate billing matrix, including explicit `legacy_stripe` account operations with unified pricing. - Update Cloud fixtures and affected account, settings, subscription, pricing, and token-flow coverage. ## AS IS Cloud workspace/auth/UI/billing paths can diverge depending on `team_workspaces_enabled`. ## TO BE Cloud workspace mode is the single path after authenticated config loads; OSS and unloaded workspace bootstrap remain legacy. No intended visual change; this is rollout retirement, so no screenshot is required. ## Regression coverage - Auth session cookie, API/WebSocket token priority, workspace restoration/switch/recovery. - Workspace gate, account UI, settings/dialog layout, create/invite loaders, pricing, and subscription success behavior. - Intermediate billing routing for OSS, unloaded Cloud, team, personal consolidated off/on, and `legacy_stripe`. - Cloud browser fixtures omit the retired key. - Exact source/browser scan confirms no `team_workspaces_enabled` or `teamWorkspacesEnabled` references. ## Validation - 14 focused Vitest files: 453 tests passed. - `pnpm typecheck` and `pnpm typecheck:browser` passed via commit hook. - targeted formatting/lint passed via commit hook. - `git diff --check dante/workspace-init-recovery...HEAD` passed. - `pnpm knip --cache` passed via push hook. - Focused Cloud Playwright: billing facade 2 passed; pricing owner flow 1 passed. The member-plan flow timed out during local app boot before assertions; the corrected full Cloud Playwright CI job passed. ## Review focus Review only the atomic team-flag removal and intermediate routing matrix. Canonical billing status and consolidated-flag retirement are intentionally deferred. ## Stack/deployment order 1. [Comfy-Org#14612](Comfy-Org#14612) — recovery foundation 2. **This PR** — retire team workspace rollout flag 3. [Comfy-Org#14614](Comfy-Org#14614) — canonical billing status and legacy rail preservation 4. [Comfy-Org#14615](Comfy-Org#14615) — retire consolidated billing rollout flag Deploy the frontend stack before the backend removes `/api/features` compatibility keys. This is also a drain requirement: age out deployed frontend versions and open tabs that still read either retired key before removing backend compatibility keys. Confirm `team_workspaces_enabled` is at 100% and update `Comfy-Org/cloud#6040` to depend on the selected stack rather than competing PR Comfy-Org#14530 before merge. After backend key removal, reverting this PR is not a behavioral restore because a reinstated read of the missing key resolves to the off branch. --------- Co-authored-by: Dante <6510430+dante01yoon@users.noreply.github.com> Co-authored-by: Amp <amp@ampcode.com>
22c2523 to
147988b
Compare
Rebased onto the rebased #14614 after #14613 landed as a squash commit. Applied with no conflicts. Verified after rebase: 14 files / +20 / -130, matching the reviewed change; zero remaining references to consolidated_billing_enabled or consolidatedBillingEnabled across src/ and browser_tests/; pnpm typecheck clean; 5 test files and 105 tests pass across composables/billing, useFeatureFlags and platform/remoteConfig. Co-Authored-By: dante01yoon <bunggl@naver.com>
ef30c0b to
241da53
Compare
…fy-Org#14614) ## Summary Part 3/4 of the workspace/billing rollout retirement stack. Standardizes Cloud subscription discovery on canonical workspace billing status while preserving explicit `legacy_stripe` account operations. Predecessor: [Comfy-Org#14613](Comfy-Org#14613). Successor: [Comfy-Org#14615](Comfy-Org#14615). ## Root cause Cloud subscription state was read from both `/customers/cloud-subscription-status` and `/api/billing/status` with different response types and cancellation fields (`end_date` versus `cancel_at`). That allowed stale rail selection and could mix workspace status/balance with legacy top-up and management behavior. ## Changes - Read Cloud subscription status only from `/api/billing/status` using the canonical type and `cancel_at`. - Avoid ingress status requests for non-Cloud builds. - Preserve `legacy_stripe` balance, top-up, and management while unified checkout remains workspace-backed. - Keep `consolidated_billing_enabled` routing intact. - Dispatch the production pending-checkout event in the Playwright helper. - Strengthen the rail regression to prove canonical discovery, no old-status request, legacy balance/credits visibility, and rejection of stale workspace zero balance. ## AS IS Two status contracts can compete, and cancellation/rail state can be stale or translated inconsistently. ## TO BE Canonical status selects the rail; explicit legacy Stripe continues legacy account operations and unified checkout. No intended visual change, so no screenshot is required. ## Regression coverage - Canonical Cloud status and non-Cloud no-ingress behavior. - Canonical cancellation in watcher and legacy adapter. - Adapter switching and stale-rail failure. - Playwright regression serves `consolidated_billing_enabled: true` + `legacy_stripe`, observes canonical status and legacy balance, proves old status absent, and verifies credits/top-up UI without stale zero balance. ## Validation - 4 focused Vitest files: 79 tests passed. - `pnpm typecheck` and `pnpm typecheck:browser` passed via commit hooks. - targeted format/lint passed via commit hooks. - `git diff --check dante/retire-team-workspace-flag...HEAD` passed. - `pnpm knip --cache` passed via push hook. - Focused Playwright legacy-rail regression passed against local Cloud dev server (1 passed). The full two-test file had one transient initial boot timeout on its first run; its second pricing-flow test passed, and the focused failed case passed on rerun. ## Review focus Review only canonical status/cancellation semantics and the legacy-account/unified-checkout boundary. Consolidated flag retirement belongs to part 4. ## Stack/deployment order 1. [Comfy-Org#14612](Comfy-Org#14612) — recovery foundation 2. [Comfy-Org#14613](Comfy-Org#14613) — retire team workspace rollout flag 3. **This PR** — canonical status and legacy rail preservation 4. [Comfy-Org#14615](Comfy-Org#14615) — retire consolidated billing rollout flag Deploy the frontend stack before the backend removes `/api/features` compatibility keys. --------- Co-authored-by: Connor Byrne <c.byrne@comfy.org> Co-authored-by: Amp <amp@ampcode.com>
Part 4/4 of the workspace and billing rollout retirement stack.
Predecessor: #14614
Successor: none (final stack PR)
Summary
Retires
consolidated_billing_enabledafter canonical billing discovery and explicit rail preservation landed in #14614. Loaded Cloud workspaces now use unified pricing, while personal workspaces onlegacy_striperetain legacy account operations.Root cause
The final billing route still depended on a compatibility feature key even after the workspace billing contract and canonical rail status were available. Removing the backend key first would therefore change personal-workspace routing during bootstrap or strand legacy Stripe balance/top-up/management behavior. The frontend needs a deterministic route based on distribution, workspace readiness/type, and the canonical billing rail before backend compatibility keys disappear.
Changes
unknown/striperails through workspace billing.legacy_stripewhile keeping unified pricing for every loaded Cloud workspace./api/features.AS IS
A loaded Cloud personal workspace still consults
consolidated_billing_enabledto decide its billing adapter and pricing route.No intended visual change. This is rollout cleanup, so no screenshot is required.
TO BE
Loaded Cloud workspaces use unified pricing without a rollout key. Canonical
billing_raildetermines whether personal account operations remain onlegacy_stripe; team and migrated/unknown personal workspaces use workspace billing.No intended visual change. The existing legacy rail UI remains available where the canonical status selects it.
Regression coverage
legacy_stripelegacy account operations plus unified pricing./api/featuresomitting both retired keys, canonical status discovery, legacy balance/credits, and no stale workspace zero balance.srcorbrowser_tests.Validation
pnpm test:unit src/composables/useFeatureFlags.test.ts src/composables/billing/useBillingRouting.test.ts src/composables/billing/useBillingContext.test.ts src/platform/remoteConfig/refreshRemoteConfig.test.ts— 83 passed.pnpm typecheck— passed.pnpm typecheck:browser— passed.pnpm knip --cachepre-push check — passed.PLAYWRIGHT_LOCAL=1 PLAYWRIGHT_TEST_URL=http://localhost:5174 pnpm test:browser browser_tests/tests/billingFacadeConsumers.spec.ts -g 'avatar popover'— 1 passed.git diff --check dante/canonical-billing-status...HEAD— passed.Review focus
Please review only this PR's removal of the consolidated flag and the final routing matrix. Canonical endpoint migration and pending-checkout stabilization belong to #14614; workspace-flag retirement belongs to #14613.
Stack/deployment order
Deploy the frontend stack in this order before the backend removes the
/api/featurescompatibility keys. Each branch remains independently deployable; do not deploy a later PR without its predecessor. This is also a drain requirement: age out deployed frontend versions and open tabs that still read either retired key before removing backend compatibility keys. Confirmconsolidated_billing_enabledis at 100% and updateComfy-Org/cloud#6040to depend on this selected stack rather than competing PR #14530 before merge. After backend key removal, reverting this PR is not a behavioral restore because a reinstated read of the missing key resolves to the off branch.