Repository navigation
Remove dead betas plumbing - #101885
Remove dead betas plumbing#101885
Conversation
The betas array is threaded through ~130 files but read in only four
places in src: Permissions.ts, usePermissions.ts, the test mock and the
beta overrides page. Everywhere else it is forwarded into parameters
that no function body reads.
Remove the four dead sinks that carried it (OpenReportActionParams.betas,
getMoneyRequestOptions and canCreateRequest, completePaymentOnboarding,
BuildPolicyDataOptions.betas) and the useOnyx(ONYXKEYS.BETAS)
subscriptions that existed only to feed them, taking those from 97 files
down to 2.
Also drop a stale {virtual: true} on the RecentlyAddedSection mock in
HomePage.test.tsx, which stopped the mock from ever resolving.
…ve-dead-betas # Conflicts: # src/libs/actions/Task.ts
|
@MelvinBot review |
Review: LGTM, with two notesThe core claim holds up. What I verified locally at
1. The
|
…l sites Both came in with the main merge and passed [CONST.BETAS.ALL] in the position that is now rules.
The mock resolves either way, so the change was unnecessary.
…ve-dead-betas # Conflicts: # src/components/MoneyRequestReportView/MoneyRequestReportActionsList.tsx
|
@MelvinBot review |
…ve-dead-betas # Conflicts: # src/pages/inbox/report/ContextMenu/BaseReportActionContextMenu.tsx
|
@MelvinBot review again after merge 😅 |
Re-review at
|
Re-review after merge: still LGTM, nothing brokeBoth earlier findings are addressed, the three merges re-applied cleanly, and everything is green at Prior findings resolved
The merges landed correctly. Main split two of the files you'd already edited, and the removal was re-applied to both:
No plumbing leaked back in. What I ran at
The last row matters most for a merge review — CI agrees: typecheck, ESLint, all 8 test shards, perf, React Compiler, knip, and codecov are green. The only red check is Conflict-resolution auditThe risk with three merges into a 167-file mechanical PR is a resolution that silently reverts a main-side change. I scanned the full merged diff for it: of 1648 changed lines, 471 contain no One nit left: the description still says I reviewed this statically and through the test suite. I could not drive the app in a browser this run, so your 11 manual test steps still need a human pass. |
The bodies no longer touch betas, so the titles described coverage that does not exist. The tests still cover the openReport and createChildReport paths, so only the names change.
getMoneyRequestInformation has no betas parameter. The property survived because baseParams is spread into the call, and spreading a declared object skips excess property checking.
Once betas is gone the test is byte-for-byte the same call and the same assertions as the BASIC approval mode test above it.
|
@huult its ready for review |
…ve-dead-betas # Conflicts: # src/libs/actions/Link.ts # src/libs/actions/Report/index.ts # src/pages/DynamicReportDetailsPage.tsx
Reviewer Checklist
Screenshots/VideosAndroid: HybridAppScreen.Recording.2026-09-24.at.12.29.59.movAndroid: mWeb ChromeScreen.Recording.2026-09-24.at.12.32.17.moviOS: HybridAppScreen.Recording.2026-09-24.at.12.33.51.moviOS: mWeb SafariScreen.Recording.2026-09-24.at.12.34.30.movMacOS: Chrome / SafariScreen.Recording.2026-09-24.at.12.16.34.mov |
|
I’ll take a look soon. |
It fits within the line limit once betas is gone, which is what oxfmt expects.
|
@huult friendly bump |
|
@arekm213 I think I can start reviewing this PR in about 2 hours |
…ve-dead-betas # Conflicts: # src/components/SettlementButton/index.tsx # src/pages/home/TimeSensitiveSection/items/UnlockBankAccount.tsx # src/pages/settings/Wallet/WalletPage/index.tsx # src/pages/workspace/workflows/tabs/WorkflowsPaymentsTab.tsx
Auto-merge kept main's canCreateRequest call, which still passed allBetas after this branch removed the parameter.
|
@arekm213 You still need to update this PR, right? If so, go ahead and make the changes. I’ll review it again afterward. |
|
@huult i had to resolve conflicts that just appeared hour ago, it has a lot of files changes so i would be happy if we could actively work on reviewing it now and get it ready to merge soon - in few minutes it will be ready for review again |
Main added this suite for the extracted hook and asserted betas was forwarded to openReport, which this branch removes.
|
@huult its ready now |
|
@MelvinBot review this |
Review at
|
| @@ -2431,7 +2431,6 @@ type CreateAndOpenSearchTransactionThreadParams = { | |||
| currentUserAccountID: number; | |||
|
|
|||
| /** Beta features list */ | |||
There was a problem hiding this comment.
@arekm213 Did you miss this comment? Please remove it.
| @@ -45,7 +45,6 @@ type WithReportOrNotFoundOnyxProps = { | |||
| policy: OnyxEntry<OnyxTypes.Policy>; | |||
|
|
|||
| /** Beta features list */ | |||
There was a problem hiding this comment.
@arekm213 Did you miss this comment? Please remove it.
|
@arekm213 Could you remove the two unused pieces of code above, then tag me to approve this PR? |
Both described the betas field that this branch already deleted.
|
@huult done! |
|
✋ This PR was not deployed to staging yet because QA is ongoing. It will be automatically deployed to staging after the next production release. |
|
🚧 mountiny has triggered a test Expensify/App build. You can view the workflow run here. |
|
🧪🧪 Use the links below to test this adhoc build on Android, iOS, and Web. Happy testing! 🧪🧪
|
|
🚀 Deployed to staging by https://github.com/mountiny in version: 9.4.94-0 🚀
|
|
No help site changes are required for this PR, so I did not create a docs PR. This PR removes an unused
Nothing in |
|
🚀 Deployed to production by https://github.com/Julesssss in version: 9.4.94-0 🚀
Bundle Size Analysis (Sentry): |
Explanation of Change
Follow up to #99444 and #100061. Those two PRs routed every beta check through
usePermissions, which made something visible: thebetasarray is now read in only four places insrc(Permissions.ts,usePermissions.ts, the beta overrides page, and the test mock). Everywhere else it is forwarded into parameters that no function body ever reads.Four dead sinks carried it:
OpenReportActionParams.betasinsrc/libs/actions/Report/index.tsgetMoneyRequestOptionsandcanCreateRequestinsrc/libs/ReportUtils.tscompletePaymentOnboardinginsrc/libs/actions/IOU/PayMoneyRequest.tsBuildPolicyDataOptions.betasinsrc/libs/actions/Policy/Policy.tsThey went dead in May, when #89469 removed the onboarding cohort gate and #91084 removed Manager McTest. Nothing flagged it, because an unused parameter sitting in the middle of a signature is invisible to both ESLint (
no-unused-varsruns withargs: 'after-used', which only reports trailing parameters) and TypeScript (noUnusedParametersis off).The cost is not only dead code. 97 files still call
useOnyx(ONYXKEYS.BETAS)purely to have something to pass along, so each of them re-renders wheneverBETASupdates. This PR takes those 97 down to 2, leaving 3 liveBETASsubscriptions in the app:createOnyxContext(ONYXKEYS.BETAS)inOnyxListItemProvider, which serves all 154isBetaEnabledcall sites throughusePermissionsDeepLinkHandler, which keeps only the loading status (see below)DynamicBetaOverridesPage, which deliberately reads the raw account valueThe one thing that is not purely mechanical
betasalso sat in the dependency list of 12 effects (10useEffect, theuseLayoutEffectinAmountSubmitDataSync, and theuseFocusEffectinConciergePage), plus oneuseCallbackthat is itself an effect dependency —fetchReportinReportAddAttachmentModalContent. That is 13 effect triggers, of which 11 no longer re-run whenBETASarrives from Onyx.The worry that deserves an answer is this:
BETASstarts outundefined, so an effect could have run early, passedundefineddown, and relied on the laterbetaschange to run again with the real value. Losing that trigger would leave something computed fromundefined.That cannot happen here, for two reasons.
Nothing downstream can read the betas value at all. It is not just unused as a parameter, there is no second channel either. Outside
useOnyx,ONYXKEYS.BETASappears in exactly three places insrc: the type declaration inONYXKEYS.ts, the key list inExportOnyxState, and thecreateOnyxContextcall inOnyxListItemProvider. There is no module levelOnyx.connectWithoutViewon it and no helper that reads it, and nothing leaked to the server either: each sink builds its APIparametersobject field by field, with no...paramsspread, and no type undersrc/libs/API/has abetasfield. SoopenReport(..., undefined)andopenReport(..., ['all', ...])do exactly the same thing, and after this PR the parameter does not exist at all.The two effects that do resolve a beta keep a reactive dependency on it.
AmountSubmitDataSyncwritesisASAPSubmitBetaEnabled: isBetaEnabled(CONST.BETAS.ASAP_SUBMIT)into a snapshot ref; its dependency array containsisBetaEnabled, whichusePermissionsreturns from auseMemokeyed on[betas, betaConfiguration, betaOverrides], so when betas load that function gets a new identity and the effect re-runs with the correct value.DeepLinkHandlerkeeps its subscription for the same reason in a different shape:betasMetadatastill gatesisLoadingOnyxValue, so only the unused value is dropped —const [, betasMetadata] = useOnyx(ONYXKEYS.BETAS). In both cases the trigger is preserved through the channel that actually determines the result, rather than through the dead array.Separately, 1
useCallbackand 2useMemodependency lists losebetasas well. Those only change how often a value is recomputed, and I checked that none of them feeds an effect.Test changes
Four tests go away, all of them duplicates or tautologies once
betasis gone:PromotedActionsBarTestthat assertedbetaswas forwarded and became exact duplicates of the neighbouringintroSelectedtestsdescribe('betas parameter handling')block inReportUtilsTestthat was mostly tautologicalcreates a new workspace when betas are explicitly passedinPolicyTest, which became a byte-for-byte duplicate of theBASIC approval modetest directly above itAn
it.eachinReportTestparameterised overwith betasandwithout betascollapsed to a plainit, since both cases became identical.Seven tests in
ReportTestare renamed. Their titles promised betas pass-through coverage that no longer exists, while the bodies still cover the realopenReportandcreateChildReportpaths, so only the names change.Finally,
GetMoneyRequestInformationTestcarriedbetas: [] as Beta[]in abaseParamsobject spread into every call, even thoughgetMoneyRequestInformationhas no such parameter. That one is a third blind spot on top of the two above: TypeScript only checks for excess properties on fresh object literals, so spreading a pre-declared object hides the dead field.Fixed Issues
$ #98409
PROPOSAL:
Tests
There is no new UI. Everything below should behave exactly as it does on main.
Steps 7 and 11 need a brand new account.
/conciergeroute, Settings > About > Report a bug) and verify each one opens the Concierge chat.Offline tests
Same as Tests. Nothing here depends on network state.
QA Steps
Same as Tests.
PR Author Checklist
### Fixed Issuessection aboveTestssectionOffline stepssectionQA stepssectionAvatar, I verified the components usingAvatarare working as expected)StyleUtils.getBackgroundAndBorderStyle(theme.componentBG))npm run compress-svg)Avataris modified, I verified thatAvataris working as expected in all cases)Designlabel and/or tagged@Expensify/designso the design team can review the changes.mainbranch was merged into this PR after a review, I tested again and verified the outcome was still expected according to theTeststeps.Screenshots/Videos
Android: Native
Android: mWeb Chrome
iOS: Native
iOS: mWeb Safari
MacOS: Chrome / Safari