From 6079046458dcb63c4f5d1a22be026d78aace6d4a Mon Sep 17 00:00:00 2001
From: Jack Senyitko <jsenyitko@expensify.com>
Date: Wed, 7 Jan 2026 11:26:01 -0500
Subject: [PATCH] revert 6020367

---
 src/CONST/index.ts                            |   1 -
 src/ROUTES.ts                                 |   4 -
 src/SCREENS.ts                                |   1 -
 .../MoneyRequestConfirmationList.tsx          |  13 --
 .../MoneyRequestConfirmationListFooter.tsx    |   7 +-
 .../ReportActionItem/MoneyRequestView.tsx     |  25 +--
 .../Search/TransactionListItem.tsx            |  20 +-
 src/hooks/useViolations.ts                    |   3 +-
 src/languages/de.ts                           |   5 -
 src/languages/en.ts                           |   5 -
 src/languages/es.ts                           |   5 -
 src/languages/fr.ts                           |   5 -
 src/languages/it.ts                           |   5 -
 src/languages/ja.ts                           |   5 -
 src/languages/nl.ts                           |   5 -
 src/languages/pl.ts                           |   5 -
 src/languages/pt-BR.ts                        |   5 -
 src/languages/zh-hans.ts                      |   5 -
 ...etPolicyCategoryAttendeesRequiredParams.ts |   7 -
 src/libs/API/parameters/index.ts              |   1 -
 src/libs/API/types.ts                         |   2 -
 src/libs/AttendeeUtils.ts                     |  92 --------
 .../ModalStackNavigators/index.tsx            |   1 -
 .../RELATIONS/WORKSPACE_TO_RHP.ts             |   1 -
 src/libs/Navigation/linkingConfig/config.ts   |   3 -
 src/libs/Navigation/types.ts                  |   4 -
 src/libs/TransactionUtils/index.ts            |   7 +-
 src/libs/Violations/ViolationsUtils.ts        |  50 -----
 src/libs/actions/IOU/index.ts                 |   4 +-
 src/libs/actions/Policy/Category.ts           |  64 ------
 .../request/step/IOURequestStepAttendees.tsx  |  23 +-
 .../categories/CategoryRequiredFieldsPage.tsx | 103 ---------
 .../categories/CategorySettingsPage.tsx       | 103 +++++----
 .../rules/IndividualExpenseRulesSection.tsx   |  27 +--
 src/types/onyx/PolicyCategory.ts              |   3 -
 tests/unit/ViolationUtilsTest.ts              | 203 ------------------
 36 files changed, 81 insertions(+), 741 deletions(-)
 delete mode 100644 src/libs/API/parameters/SetPolicyCategoryAttendeesRequiredParams.ts
 delete mode 100644 src/libs/AttendeeUtils.ts
 delete mode 100644 src/pages/workspace/categories/CategoryRequiredFieldsPage.tsx

diff --git a/src/CONST/index.ts b/src/CONST/index.ts
index 4d7031ce388d..525b18be51a3 100755
--- a/src/CONST/index.ts
+++ b/src/CONST/index.ts
@@ -5768,7 +5768,6 @@ const CONST = {
         RECEIPT_GENERATED_WITH_AI: 'receiptGeneratedWithAI',
         OVER_TRIP_LIMIT: 'overTripLimit',
         COMPANY_CARD_REQUIRED: 'companyCardRequired',
-        MISSING_ATTENDEES: 'missingAttendees',
     },
     RTER_VIOLATION_TYPES: {
         BROKEN_CARD_CONNECTION: 'brokenCardConnection',
diff --git a/src/ROUTES.ts b/src/ROUTES.ts
index 9da4f186d1c7..29776e8738ae 100644
--- a/src/ROUTES.ts
+++ b/src/ROUTES.ts
@@ -1977,10 +1977,6 @@ const ROUTES = {
         route: 'workspaces/:policyID/category/:categoryName/require-receipts-over',
         getRoute: (policyID: string, categoryName: string) => `workspaces/${policyID}/category/${encodeURIComponent(categoryName)}/require-receipts-over` as const,
     },
-    WORKSPACE_CATEGORY_REQUIRED_FIELDS: {
-        route: 'workspaces/:policyID/category/:categoryName/required-fields',
-        getRoute: (policyID: string, categoryName: string) => `workspaces/${policyID}/category/${encodeURIComponent(categoryName)}/required-fields` as const,
-    },
     WORKSPACE_CATEGORY_APPROVER: {
         route: 'workspaces/:policyID/category/:categoryName/approver',
         getRoute: (policyID: string, categoryName: string) => `workspaces/${policyID}/category/${encodeURIComponent(categoryName)}/approver` as const,
diff --git a/src/SCREENS.ts b/src/SCREENS.ts
index ef2ba954b794..8f763eda3ce5 100644
--- a/src/SCREENS.ts
+++ b/src/SCREENS.ts
@@ -690,7 +690,6 @@ const SCREENS = {
         CATEGORY_DESCRIPTION_HINT: 'Category_Description_Hint',
         CATEGORY_APPROVER: 'Category_Approver',
         CATEGORY_REQUIRE_RECEIPTS_OVER: 'Category_Require_Receipts_Over',
-        CATEGORY_REQUIRED_FIELDS: 'Category_Required_Fields',
         CATEGORIES_SETTINGS: 'Categories_Settings',
         CATEGORIES_IMPORT: 'Categories_Import',
         CATEGORIES_IMPORTED: 'Categories_Imported',
diff --git a/src/components/MoneyRequestConfirmationList.tsx b/src/components/MoneyRequestConfirmationList.tsx
index d11f607158c4..303955628739 100755
--- a/src/components/MoneyRequestConfirmationList.tsx
+++ b/src/components/MoneyRequestConfirmationList.tsx
@@ -30,7 +30,6 @@ import {
     setMoneyRequestTaxRate,
     setSplitShares,
 } from '@libs/actions/IOU';
-import {getIsMissingAttendeesViolation} from '@libs/AttendeeUtils';
 import {isCategoryDescriptionRequired} from '@libs/CategoryUtils';
 import {convertToBackendAmount, convertToDisplayString, convertToDisplayStringWithoutCurrency, getCurrencyDecimals} from '@libs/CurrencyUtils';
 import DistanceRequestUtils from '@libs/DistanceRequestUtils';
@@ -921,12 +920,6 @@ function MoneyRequestConfirmationList({
                 return;
             }
 
-            const isMissingAttendeesViolation = getIsMissingAttendeesViolation(policyCategories, iouCategory, iouAttendees, currentUserPersonalDetails, policy?.isAttendeeTrackingEnabled);
-            if (isMissingAttendeesViolation) {
-                setFormError('violations.missingAttendees');
-                return;
-            }
-
             if (isPerDiemRequest && (transaction.comment?.customUnit?.subRates ?? []).length === 0) {
                 setFormError('iou.error.invalidSubrateLength');
                 return;
@@ -1000,8 +993,6 @@ function MoneyRequestConfirmationList({
             showDelegateNoAccessModal,
             iouCategory,
             policyCategories,
-            iouAttendees,
-            currentUserPersonalDetails,
         ],
     );
 
@@ -1025,10 +1016,6 @@ function MoneyRequestConfirmationList({
         if (isTypeSplit && !shouldShowReadOnlySplits) {
             return debouncedFormError && translate(debouncedFormError);
         }
-        // Don't show error at the bottom of the form for missing attendees
-        if (formError === 'violations.missingAttendees') {
-            return;
-        }
         return formError && translate(formError);
     }, [routeError, isTypeSplit, shouldShowReadOnlySplits, debouncedFormError, formError, translate]);
 
diff --git a/src/components/MoneyRequestConfirmationListFooter.tsx b/src/components/MoneyRequestConfirmationListFooter.tsx
index da70f62205cd..4e4acba9a7df 100644
--- a/src/components/MoneyRequestConfirmationListFooter.tsx
+++ b/src/components/MoneyRequestConfirmationListFooter.tsx
@@ -380,7 +380,6 @@ function MoneyRequestConfirmationListFooter({
     const shouldDisplayDistanceRateError = formError === 'iou.error.invalidRate';
     const shouldDisplayTagError = formError === 'violations.tagOutOfPolicy';
     const shouldDisplayCategoryError = formError === 'violations.categoryOutOfPolicy';
-    const shouldDisplayAttendeesError = formError === 'violations.missingAttendees';
 
     const showReceiptEmptyState = shouldShowReceiptEmptyState(iouType, action, policy, isPerDiemRequest);
     // The per diem custom unit
@@ -728,7 +727,7 @@ function MoneyRequestConfirmationListFooter({
             item: (
                 <MenuItemWithTopDescription
                     key="attendees"
-                    shouldShowRightIcon={!isReadOnly}
+                    shouldShowRightIcon
                     title={iouAttendees?.map((item) => item?.displayName ?? item?.login).join(', ')}
                     description={`${translate('iou.attendees')} ${
                         iouAttendees?.length && iouAttendees.length > 1 && formattedAmountPerAttendee ? `\u00B7 ${formattedAmountPerAttendee} ${translate('common.perPerson')}` : ''
@@ -742,10 +741,8 @@ function MoneyRequestConfirmationListFooter({
 
                         Navigation.navigate(ROUTES.MONEY_REQUEST_ATTENDEE.getRoute(action, iouType, transactionID, reportID, Navigation.getActiveRoute()));
                     }}
-                    interactive={!isReadOnly}
+                    interactive
                     shouldRenderAsHTML
-                    brickRoadIndicator={shouldDisplayAttendeesError ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : undefined}
-                    errorText={shouldDisplayAttendeesError ? translate(formError) : ''}
                 />
             ),
             shouldShow: shouldShowAttendees,
diff --git a/src/components/ReportActionItem/MoneyRequestView.tsx b/src/components/ReportActionItem/MoneyRequestView.tsx
index 70fee416f672..432fa62a66f7 100644
--- a/src/components/ReportActionItem/MoneyRequestView.tsx
+++ b/src/components/ReportActionItem/MoneyRequestView.tsx
@@ -32,8 +32,6 @@ import useThemeStyles from '@hooks/useThemeStyles';
 import useTransactionViolations from '@hooks/useTransactionViolations';
 import type {ViolationField} from '@hooks/useViolations';
 import useViolations from '@hooks/useViolations';
-import {initSplitExpense, updateMoneyRequestBillable, updateMoneyRequestReimbursable} from '@libs/actions/IOU/index';
-import {getIsMissingAttendeesViolation} from '@libs/AttendeeUtils';
 import {filterPersonalCards, getCompanyCardDescription} from '@libs/CardUtils';
 import {getDecodedCategoryName, isCategoryMissing} from '@libs/CategoryUtils';
 import {convertToDisplayString} from '@libs/CurrencyUtils';
@@ -53,12 +51,13 @@ import {
     isTaxTrackingEnabled,
 } from '@libs/PolicyUtils';
 import {getOriginalMessage, isMoneyRequestAction} from '@libs/ReportActionsUtils';
-import {computeReportName} from '@libs/ReportNameUtils';
 import {isSplitAction} from '@libs/ReportSecondaryActionUtils';
 import {
     canEditFieldOfMoneyRequest,
     canEditMoneyRequest,
     canUserPerformWriteAction as canUserPerformWriteActionReportUtils,
+    // eslint-disable-next-line @typescript-eslint/no-deprecated
+    getReportName,
     getTransactionDetails,
     getTripIDFromTransactionParentReportID,
     isExpenseReport,
@@ -98,6 +97,7 @@ import {
 import ViolationsUtils from '@libs/Violations/ViolationsUtils';
 import Navigation from '@navigation/Navigation';
 import AnimatedEmptyStateBackground from '@pages/home/report/AnimatedEmptyStateBackground';
+import {initSplitExpense, updateMoneyRequestBillable, updateMoneyRequestReimbursable} from '@userActions/IOU';
 import CONST from '@src/CONST';
 import type {TranslationPaths} from '@src/languages/types';
 import ONYXKEYS from '@src/ONYXKEYS';
@@ -167,7 +167,6 @@ function MoneyRequestView({
     const {getReportRHPActiveRoute} = useActiveRoute();
     const [lastVisitedPath] = useOnyx(ONYXKEYS.LAST_VISITED_PATH, {canBeMissing: true});
 
-    const [allPolicies] = useOnyx(ONYXKEYS.COLLECTION.POLICY, {canBeMissing: true});
     const [allTransactions] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION, {canBeMissing: false});
 
     const searchContext = useSearchContext();
@@ -473,13 +472,6 @@ function MoneyRequestView({
     const hasErrors = hasMissingSmartscanFields(transaction);
     // Need to return undefined when we have pendingAction to avoid the duplicate pending action
     const getPendingFieldAction = (fieldPath: TransactionPendingFieldsKey) => (pendingAction ? undefined : transaction?.pendingFields?.[fieldPath]);
-    const isMissingAttendeesViolation = getIsMissingAttendeesViolation(
-        policyCategories,
-        updatedTransaction?.category ?? categoryForDisplay,
-        actualAttendees,
-        currentUserPersonalDetails,
-        policy?.isAttendeeTrackingEnabled,
-    );
 
     const getErrorForField = (field: ViolationField, data?: OnyxTypes.TransactionViolation['data'], policyHasDependentTags = false, tagValue?: string) => {
         // Checks applied when creating a new expense
@@ -520,10 +512,6 @@ function MoneyRequestView({
             return `${violations.map((violation) => ViolationsUtils.getViolationTranslation(violation, translate, canEdit, undefined, companyCardPageURL)).join('. ')}.`;
         }
 
-        if (field === 'attendees' && isMissingAttendeesViolation) {
-            return translate('violations.missingAttendees');
-        }
-
         return '';
     };
 
@@ -732,9 +720,8 @@ function MoneyRequestView({
         );
     });
 
-    const reportNameToDisplay = isFromMergeTransaction
-        ? (updatedTransaction?.reportName ?? translate('common.none'))
-        : (parentReport?.reportName ?? computeReportName(parentReport, allReports, allPolicies, allTransactions));
+    // eslint-disable-next-line @typescript-eslint/no-deprecated
+    const reportNameToDisplay = isFromMergeTransaction ? (updatedTransaction?.reportName ?? translate('common.none')) : (parentReport?.reportName ?? getReportName(parentReport));
     const shouldShowReport = !!parentReportID || (isFromMergeTransaction && !!reportNameToDisplay);
     const reportCopyValue = !canEditReport && reportNameToDisplay !== translate('common.none') ? reportNameToDisplay : undefined;
     const shouldShowCategoryAnalyzing = isCategoryBeingAnalyzed(updatedTransaction ?? transaction);
@@ -1025,8 +1012,6 @@ function MoneyRequestView({
                             onPress={() => {
                                 Navigation.navigate(ROUTES.MONEY_REQUEST_ATTENDEE.getRoute(CONST.IOU.ACTION.EDIT, iouType, transaction.transactionID, transactionThreadReport?.reportID));
                             }}
-                            brickRoadIndicator={getErrorForField('attendees') ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : undefined}
-                            errorText={getErrorForField('attendees')}
                             interactive={canEdit}
                             shouldShowRightIcon={canEdit}
                             shouldRenderAsHTML
diff --git a/src/components/SelectionListWithSections/Search/TransactionListItem.tsx b/src/components/SelectionListWithSections/Search/TransactionListItem.tsx
index 30c9da177f41..ef398b51994a 100644
--- a/src/components/SelectionListWithSections/Search/TransactionListItem.tsx
+++ b/src/components/SelectionListWithSections/Search/TransactionListItem.tsx
@@ -21,7 +21,6 @@ import useTheme from '@hooks/useTheme';
 import useThemeStyles from '@hooks/useThemeStyles';
 import type {TransactionPreviewData} from '@libs/actions/Search';
 import {handleActionButtonPress as handleActionButtonPressUtil} from '@libs/actions/Search';
-import {syncMissingAttendeesViolation} from '@libs/AttendeeUtils';
 import getNonEmptyStringOnyxID from '@libs/getNonEmptyStringOnyxID';
 import {isViolationDismissed, shouldShowViolation} from '@libs/TransactionUtils';
 import variables from '@styles/variables';
@@ -66,8 +65,6 @@ function TransactionListItem<TItem extends ListItem>({
     const snapshotPolicy = useMemo(() => {
         return (snapshot?.data?.[`${ONYXKEYS.COLLECTION.POLICY}${transactionItem.policyID}`] ?? {}) as Policy;
     }, [snapshot, transactionItem.policyID]);
-    // Fetch policy categories directly from Onyx since they are not included in the search snapshot
-    const [policyCategories] = originalUseOnyx(`${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${transactionItem.policyID}`, {canBeMissing: true});
     const [lastPaymentMethod] = useOnyx(`${ONYXKEYS.NVP_LAST_PAYMENT_METHOD}`, {canBeMissing: true});
     const [personalPolicyID] = useOnyx(ONYXKEYS.PERSONAL_POLICY_ID, {canBeMissing: true});
 
@@ -124,25 +121,12 @@ function TransactionListItem<TItem extends ListItem>({
     ]);
 
     const transactionViolations = useMemo(() => {
-        const onyxViolations = (violations?.[`${ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS}${transactionItem.transactionID}`] ?? []).filter(
+        return (violations?.[`${ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS}${transactionItem.transactionID}`] ?? []).filter(
             (violation: TransactionViolation) =>
                 !isViolationDismissed(transactionItem, violation, currentUserDetails.email ?? '', currentUserDetails.accountID, snapshotReport, snapshotPolicy) &&
                 shouldShowViolation(snapshotReport, snapshotPolicy, violation.name, currentUserDetails.email ?? '', false, transactionItem),
         );
-
-        // Sync missingAttendees violation with current policy category settings (can be removed later when BE handles this)
-        const attendeeOnyxViolations = syncMissingAttendeesViolation(
-            onyxViolations,
-            policyCategories,
-            transactionItem.category ?? '',
-            transactionItem.attendees,
-            currentUserDetails,
-            snapshotPolicy?.isAttendeeTrackingEnabled ?? false,
-            snapshotPolicy?.type === CONST.POLICY.TYPE.CORPORATE,
-        );
-
-        return [...onyxViolations, ...attendeeOnyxViolations];
-    }, [snapshotPolicy, policyCategories, snapshotReport, transactionItem, violations, currentUserDetails.email, currentUserDetails.accountID, currentUserDetails]);
+    }, [snapshotPolicy, snapshotReport, transactionItem, violations, currentUserDetails.email, currentUserDetails.accountID]);
 
     const {isDelegateAccessRestricted, showDelegateNoAccessModal} = useContext(DelegateNoAccessContext);
 
diff --git a/src/hooks/useViolations.ts b/src/hooks/useViolations.ts
index acd96ca0987a..f86a875b35ed 100644
--- a/src/hooks/useViolations.ts
+++ b/src/hooks/useViolations.ts
@@ -6,7 +6,7 @@ import type {TransactionViolation, ViolationName} from '@src/types/onyx';
 /**
  * Names of Fields where violations can occur.
  */
-const validationFields = ['amount', 'billable', 'category', 'comment', 'date', 'merchant', 'receipt', 'tag', 'tax', 'attendees', 'customUnitRateID', 'none'] as const;
+const validationFields = ['amount', 'billable', 'category', 'comment', 'date', 'merchant', 'receipt', 'tag', 'tax', 'customUnitRateID', 'none'] as const;
 
 type ViolationField = TupleToUnion<typeof validationFields>;
 
@@ -28,7 +28,6 @@ const violationNameToField: Record<ViolationName, (violation: TransactionViolati
     maxAge: () => 'date',
     missingCategory: () => 'category',
     missingComment: () => 'comment',
-    missingAttendees: () => 'attendees',
     missingTag: () => 'tag',
     modifiedAmount: () => 'amount',
     modifiedDate: () => 'date',
diff --git a/src/languages/de.ts b/src/languages/de.ts
index 655b2ef688ae..37b4d4951931 100644
--- a/src/languages/de.ts
+++ b/src/languages/de.ts
@@ -6237,10 +6237,6 @@ Fordere Spesendetails wie Belege und Beschreibungen an, lege Limits und Standard
                 title: 'Kategorierichtlinien',
                 approver: 'Genehmiger',
                 requireDescription: 'Beschreibung erforderlich',
-                requireFields: 'Felder verpflichtend machen',
-                requiredFieldsTitle: 'Pflichtfelder',
-                requiredFieldsDescription: (categoryName: string) => `Dies gilt für alle Ausgaben, die als <strong>${categoryName}</strong> kategorisiert sind.`,
-                requireAttendees: 'Teilnehmer erforderlich machen',
                 descriptionHint: 'Hinweis zur Beschreibung',
                 descriptionHintDescription: (categoryName: string) =>
                     `Mitarbeitende daran erinnern, zusätzliche Informationen für Ausgaben der Kategorie „${categoryName}“ anzugeben. Dieser Hinweis erscheint im Beschreibungsfeld von Ausgaben.`,
@@ -7293,7 +7289,6 @@ Fordere Spesendetails wie Belege und Beschreibungen an, lege Limits und Standard
         maxAge: ({maxAge}: ViolationsMaxAgeParams) => `Datum älter als ${maxAge} Tage`,
         missingCategory: 'Fehlende Kategorie',
         missingComment: 'Beschreibung für ausgewählte Kategorie erforderlich',
-        missingAttendees: 'Für diese Kategorie sind mehrere Teilnehmer erforderlich',
         missingTag: ({tagName}: ViolationsMissingTagParams = {}) => `Fehlende ${tagName ?? 'Tag'}`,
         modifiedAmount: ({type, displayPercentVariance}: ViolationsModifiedAmountParams) => {
             switch (type) {
diff --git a/src/languages/en.ts b/src/languages/en.ts
index f53a0ff4a13c..9e06efe60f42 100755
--- a/src/languages/en.ts
+++ b/src/languages/en.ts
@@ -6097,10 +6097,6 @@ const translations = {
                 title: 'Category rules',
                 approver: 'Approver',
                 requireDescription: 'Require description',
-                requireFields: 'Require fields',
-                requiredFieldsTitle: 'Required fields',
-                requiredFieldsDescription: (categoryName: string) => `This will apply to all expenses categorized as <strong>${categoryName}</strong>.`,
-                requireAttendees: 'Require attendees',
                 descriptionHint: 'Description hint',
                 descriptionHintDescription: (categoryName: string) =>
                     `Remind employees to provide additional information for “${categoryName}” spend. This hint appears in the description field on expenses.`,
@@ -7185,7 +7181,6 @@ const translations = {
         maxAge: ({maxAge}: ViolationsMaxAgeParams) => `Date older than ${maxAge} days`,
         missingCategory: 'Missing category',
         missingComment: 'Description required for selected category',
-        missingAttendees: 'Multiple attendees required for this category',
         missingTag: ({tagName}: ViolationsMissingTagParams = {}) => `Missing ${tagName ?? 'tag'}`,
         modifiedAmount: ({type, displayPercentVariance}: ViolationsModifiedAmountParams) => {
             switch (type) {
diff --git a/src/languages/es.ts b/src/languages/es.ts
index b64f827a0369..188aa4a71bc2 100644
--- a/src/languages/es.ts
+++ b/src/languages/es.ts
@@ -5834,10 +5834,6 @@ ${amount} para ${merchant} - ${date}`,
                 title: 'Reglas de categoría',
                 approver: 'Aprobador',
                 requireDescription: 'Requerir descripción',
-                requireFields: 'Requerir campos',
-                requiredFieldsTitle: 'Campos obligatorios',
-                requiredFieldsDescription: (categoryName) => `Esto se aplicará a todos los gastos categorizados como <strong>${categoryName}</strong>.`,
-                requireAttendees: 'Requerir asistentes',
                 descriptionHint: 'Sugerencia de descripción',
                 descriptionHintDescription: (categoryName) =>
                     `Recuerda a los empleados que deben proporcionar información adicional para los gastos de “${categoryName}”. Esta sugerencia aparece en el campo de descripción en los gastos.`,
@@ -7325,7 +7321,6 @@ ${amount} para ${merchant} - ${date}`,
         maxAge: ({maxAge}) => `Fecha de más de ${maxAge} días`,
         missingCategory: 'Falta categoría',
         missingComment: 'Descripción obligatoria para la categoría seleccionada',
-        missingAttendees: 'Se requieren múltiples asistentes para esta categoría',
         missingTag: ({tagName} = {}) => `Falta ${tagName ?? 'etiqueta'}`,
         modifiedAmount: ({type, displayPercentVariance}) => {
             switch (type) {
diff --git a/src/languages/fr.ts b/src/languages/fr.ts
index e9028ea3f833..d71c334aca3c 100644
--- a/src/languages/fr.ts
+++ b/src/languages/fr.ts
@@ -6246,10 +6246,6 @@ Exigez des informations de dépense comme les reçus et les descriptions, défin
                 title: 'Règles de catégorie',
                 approver: 'Approbateur',
                 requireDescription: 'Description requise',
-                requireFields: 'Rendre les champs obligatoires',
-                requiredFieldsTitle: 'Champs obligatoires',
-                requiredFieldsDescription: (categoryName: string) => `Cela s’appliquera à toutes les dépenses classées dans la catégorie <strong>${categoryName}</strong>.`,
-                requireAttendees: 'Exiger des participants',
                 descriptionHint: 'Indice de description',
                 descriptionHintDescription: (categoryName: string) =>
                     `Rappelez aux employés de fournir des informations supplémentaires pour les dépenses « ${categoryName} ». Cet indice apparaît dans le champ de description des dépenses.`,
@@ -7304,7 +7300,6 @@ Exigez des informations de dépense comme les reçus et les descriptions, défin
         maxAge: ({maxAge}: ViolationsMaxAgeParams) => `Date de plus de ${maxAge} jours`,
         missingCategory: 'Catégorie manquante',
         missingComment: 'Description requise pour la catégorie sélectionnée',
-        missingAttendees: 'Plusieurs participants sont requis pour cette catégorie',
         missingTag: ({tagName}: ViolationsMissingTagParams = {}) => `Manquant ${tagName ?? 'étiquette'}`,
         modifiedAmount: ({type, displayPercentVariance}: ViolationsModifiedAmountParams) => {
             switch (type) {
diff --git a/src/languages/it.ts b/src/languages/it.ts
index f41517a1c156..f1c2c9e0d2af 100644
--- a/src/languages/it.ts
+++ b/src/languages/it.ts
@@ -6219,10 +6219,6 @@ Richiedi dettagli di spesa come ricevute e descrizioni, imposta limiti e valori
                 title: 'Regole di categoria',
                 approver: 'Approvatore',
                 requireDescription: 'Richiedi descrizione',
-                requireFields: 'Rendi obbligatori i campi',
-                requiredFieldsTitle: 'Campi obbligatori',
-                requiredFieldsDescription: (categoryName: string) => `Questo si applicherà a tutte le spese classificate come <strong>${categoryName}</strong>.`,
-                requireAttendees: 'Richiedi partecipanti',
                 descriptionHint: 'Suggerimento per la descrizione',
                 descriptionHintDescription: (categoryName: string) =>
                     `Ricorda ai dipendenti di fornire informazioni aggiuntive per la spesa in “${categoryName}”. Questo suggerimento appare nel campo descrizione sulle spese.`,
@@ -7279,7 +7275,6 @@ Richiedi dettagli di spesa come ricevute e descrizioni, imposta limiti e valori
         maxAge: ({maxAge}: ViolationsMaxAgeParams) => `Data precedente a ${maxAge} giorni`,
         missingCategory: 'Categoria mancante',
         missingComment: 'Descrizione richiesta per la categoria selezionata',
-        missingAttendees: 'Più partecipanti obbligatori per questa categoria',
         missingTag: ({tagName}: ViolationsMissingTagParams = {}) => `Manca ${tagName ?? 'etichetta'}`,
         modifiedAmount: ({type, displayPercentVariance}: ViolationsModifiedAmountParams) => {
             switch (type) {
diff --git a/src/languages/ja.ts b/src/languages/ja.ts
index 4df06c43524a..51c8e375b647 100644
--- a/src/languages/ja.ts
+++ b/src/languages/ja.ts
@@ -6176,10 +6176,6 @@ ${reportName}
                 title: 'カテゴリルール',
                 approver: '承認者',
                 requireDescription: '説明を必須にする',
-                requireFields: 'フィールドを必須にする',
-                requiredFieldsTitle: '必須項目',
-                requiredFieldsDescription: (categoryName: string) => `これは<strong>${categoryName}</strong>として分類されたすべての経費に適用されます。`,
-                requireAttendees: '参加者の入力を必須にする',
                 descriptionHint: '説明のヒント',
                 descriptionHintDescription: (categoryName: string) => `従業員に「${categoryName}」での支出について追加情報を提供するよう促します。このヒントは経費の説明欄に表示されます。`,
                 descriptionHintLabel: 'ヒント',
@@ -7221,7 +7217,6 @@ ${reportName}
         maxAge: ({maxAge}: ViolationsMaxAgeParams) => `${maxAge}日より前の日付`,
         missingCategory: 'カテゴリ未設定',
         missingComment: '選択したカテゴリーには説明が必要です',
-        missingAttendees: 'このカテゴリには複数の参加者が必要です',
         missingTag: ({tagName}: ViolationsMissingTagParams = {}) => `${tagName ?? 'タグ'} が見つかりません`,
         modifiedAmount: ({type, displayPercentVariance}: ViolationsModifiedAmountParams) => {
             switch (type) {
diff --git a/src/languages/nl.ts b/src/languages/nl.ts
index c3304f1df68b..a0b615c23bc5 100644
--- a/src/languages/nl.ts
+++ b/src/languages/nl.ts
@@ -6208,10 +6208,6 @@ Vraag verplichte uitgavedetails zoals bonnetjes en beschrijvingen, stel limieten
                 title: 'Categorisatieregels',
                 approver: 'Fiatteur',
                 requireDescription: 'Beschrijving vereist',
-                requireFields: 'Velden verplicht stellen',
-                requiredFieldsTitle: 'Verplichte velden',
-                requiredFieldsDescription: (categoryName: string) => `Dit is van toepassing op alle uitgaven die zijn gecategoriseerd als <strong>${categoryName}</strong>.`,
-                requireAttendees: 'Aanwezigen verplicht stellen',
                 descriptionHint: 'Beschrijvingstip',
                 descriptionHintDescription: (categoryName: string) =>
                     `Herinner medewerkers eraan om extra informatie te geven voor uitgaven in de categorie “${categoryName}”. Deze tip verschijnt in het omschrijvingsveld van uitgaven.`,
@@ -7265,7 +7261,6 @@ Vraag verplichte uitgavedetails zoals bonnetjes en beschrijvingen, stel limieten
         maxAge: ({maxAge}: ViolationsMaxAgeParams) => `Datum ouder dan ${maxAge} dagen`,
         missingCategory: 'Ontbrekende categorie',
         missingComment: 'Beschrijving vereist voor geselecteerde categorie',
-        missingAttendees: 'Meerdere deelnemers vereist voor deze categorie',
         missingTag: ({tagName}: ViolationsMissingTagParams = {}) => `Ontbreekt ${tagName ?? 'label'}`,
         modifiedAmount: ({type, displayPercentVariance}: ViolationsModifiedAmountParams) => {
             switch (type) {
diff --git a/src/languages/pl.ts b/src/languages/pl.ts
index 0ebc19d0da7b..f4c74cf922d2 100644
--- a/src/languages/pl.ts
+++ b/src/languages/pl.ts
@@ -6199,10 +6199,6 @@ Wymagaj szczegółów wydatków, takich jak paragony i opisy, ustawiaj limity i
                 title: 'Zasady kategorii',
                 approver: 'Akceptujący',
                 requireDescription: 'Wymagaj opisu',
-                requireFields: 'Wymagaj pól',
-                requiredFieldsTitle: 'Wymagane pola',
-                requiredFieldsDescription: (categoryName: string) => `To będzie miało zastosowanie do wszystkich wydatków skategoryzowanych jako <strong>${categoryName}</strong>.`,
-                requireAttendees: 'Wymagaj uczestników',
                 descriptionHint: 'Podpowiedź opisu',
                 descriptionHintDescription: (categoryName: string) =>
                     `Przypominaj pracownikom o podaniu dodatkowych informacji dotyczących wydatków w kategorii „${categoryName}”. Ta podpowiedź pojawia się w polu opisu przy wydatkach.`,
@@ -7253,7 +7249,6 @@ Wymagaj szczegółów wydatków, takich jak paragony i opisy, ustawiaj limity i
         maxAge: ({maxAge}: ViolationsMaxAgeParams) => `Data starsza niż ${maxAge} dni`,
         missingCategory: 'Brak kategorii',
         missingComment: 'Opis jest wymagany dla wybranej kategorii',
-        missingAttendees: 'Wymaganych jest wielu uczestników dla tej kategorii',
         missingTag: ({tagName}: ViolationsMissingTagParams = {}) => `Brakujące ${tagName ?? 'tag'}`,
         modifiedAmount: ({type, displayPercentVariance}: ViolationsModifiedAmountParams) => {
             switch (type) {
diff --git a/src/languages/pt-BR.ts b/src/languages/pt-BR.ts
index b8e5ee9eb465..ffbf6b1ed20e 100644
--- a/src/languages/pt-BR.ts
+++ b/src/languages/pt-BR.ts
@@ -6201,10 +6201,6 @@ Exija detalhes de despesas como recibos e descrições, defina limites e padrõe
                 title: 'Regras de categoria',
                 approver: 'Aprovador',
                 requireDescription: 'Exigir descrição',
-                requireFields: 'Exigir campos',
-                requiredFieldsTitle: 'Campos obrigatórios',
-                requiredFieldsDescription: (categoryName: string) => `Isso será aplicado a todas as despesas categorizadas como <strong>${categoryName}</strong>.`,
-                requireAttendees: 'Exigir participantes',
                 descriptionHint: 'Dica de descrição',
                 descriptionHintDescription: (categoryName: string) =>
                     `Lembre os funcionários de fornecer informações adicionais para gastos em “${categoryName}”. Essa dica aparece no campo de descrição das despesas.`,
@@ -7257,7 +7253,6 @@ Exija detalhes de despesas como recibos e descrições, defina limites e padrõe
         maxAge: ({maxAge}: ViolationsMaxAgeParams) => `Data anterior a ${maxAge} dias`,
         missingCategory: 'Categoria ausente',
         missingComment: 'Descrição obrigatória para a categoria selecionada',
-        missingAttendees: 'Vários participantes são obrigatórios para esta categoria',
         missingTag: ({tagName}: ViolationsMissingTagParams = {}) => `Faltando ${tagName ?? 'Tag'}`,
         modifiedAmount: ({type, displayPercentVariance}: ViolationsModifiedAmountParams) => {
             switch (type) {
diff --git a/src/languages/zh-hans.ts b/src/languages/zh-hans.ts
index 5e338a1dca40..df5b8f8724ab 100644
--- a/src/languages/zh-hans.ts
+++ b/src/languages/zh-hans.ts
@@ -6078,10 +6078,6 @@ ${reportName}
                 title: '类别规则',
                 approver: '审批人',
                 requireDescription: '要求描述',
-                requireFields: '必填字段',
-                requiredFieldsTitle: '必填项',
-                requiredFieldsDescription: (categoryName: string) => `这将适用于所有被归类为 <strong>${categoryName}</strong> 的费用。`,
-                requireAttendees: '要求与会者',
                 descriptionHint: '描述提示',
                 descriptionHintDescription: (categoryName: string) => `提醒员工为“${categoryName}”支出提供更多信息。此提示将显示在报销单的描述字段中。`,
                 descriptionHintLabel: '提示',
@@ -7105,7 +7101,6 @@ ${reportName}
         maxAge: ({maxAge}: ViolationsMaxAgeParams) => `日期早于 ${maxAge} 天`,
         missingCategory: '缺少类别',
         missingComment: '所选类别需要填写描述',
-        missingAttendees: '此类别需要多个参与者',
         missingTag: ({tagName}: ViolationsMissingTagParams = {}) => `缺少 ${tagName ?? '标签'}`,
         modifiedAmount: ({type, displayPercentVariance}: ViolationsModifiedAmountParams) => {
             switch (type) {
diff --git a/src/libs/API/parameters/SetPolicyCategoryAttendeesRequiredParams.ts b/src/libs/API/parameters/SetPolicyCategoryAttendeesRequiredParams.ts
deleted file mode 100644
index abaa94f241cc..000000000000
--- a/src/libs/API/parameters/SetPolicyCategoryAttendeesRequiredParams.ts
+++ /dev/null
@@ -1,7 +0,0 @@
-type SetPolicyCategoryAttendeesRequiredParams = {
-    policyID: string;
-    categoryName: string;
-    areAttendeesRequired: boolean;
-};
-
-export default SetPolicyCategoryAttendeesRequiredParams;
diff --git a/src/libs/API/parameters/index.ts b/src/libs/API/parameters/index.ts
index 2738d1fda1e9..bece054a0237 100644
--- a/src/libs/API/parameters/index.ts
+++ b/src/libs/API/parameters/index.ts
@@ -345,7 +345,6 @@ export type {default as AddDelegateParams} from './AddDelegateParams';
 export type {default as UpdateDelegateRoleParams} from './UpdateDelegateRoleParams';
 export type {default as OpenCardDetailsPageParams} from './OpenCardDetailsPageParams';
 export type {default as SetPolicyCategoryDescriptionRequiredParams} from './SetPolicyCategoryDescriptionRequiredParams';
-export type {default as SetPolicyCategoryAttendeesRequiredParams} from './SetPolicyCategoryAttendeesRequiredParams';
 export type {default as SetPolicyCategoryApproverParams} from './SetPolicyCategoryApproverParams';
 export type {default as SetWorkspaceCategoryDescriptionHintParams} from './SetWorkspaceCategoryDescriptionHintParams';
 export type {default as SetPolicyCategoryTaxParams} from './SetPolicyCategoryTaxParams';
diff --git a/src/libs/API/types.ts b/src/libs/API/types.ts
index aa3b948ae878..0e2fca6d06cd 100644
--- a/src/libs/API/types.ts
+++ b/src/libs/API/types.ts
@@ -263,7 +263,6 @@ const WRITE_COMMANDS = {
     SET_POLICY_ATTENDEE_TRACKING_ENABLED: 'SetPolicyAttendeeTrackingEnabled',
     SET_POLICY_REQUIRE_COMPANY_CARDS_ENABLED: 'SetPolicyRequireCompanyCardsEnabled',
     SET_POLICY_CATEGORY_DESCRIPTION_REQUIRED: 'SetPolicyCategoryDescriptionRequired',
-    SET_POLICY_CATEGORY_ATTENDEES_REQUIRED: 'SetPolicyCategoryAttendeesRequired',
     SET_WORKSPACE_CATEGORY_DESCRIPTION_HINT: 'SetWorkspaceCategoryDescriptionHint',
     SET_POLICY_CATEGORY_RECEIPTS_REQUIRED: 'SetPolicyCategoryReceiptsRequired',
     REMOVE_POLICY_CATEGORY_RECEIPTS_REQUIRED: 'RemoveWorkspaceCategoryReceiptsRequired',
@@ -790,7 +789,6 @@ type WriteCommandParameters = {
     [WRITE_COMMANDS.SET_POLICY_RULES_ENABLED]: Parameters.SetPolicyRulesEnabledParams;
     [WRITE_COMMANDS.SET_POLICY_REQUIRE_COMPANY_CARDS_ENABLED]: Parameters.SetPolicyRequireCompanyCardsEnabledParams;
     [WRITE_COMMANDS.SET_POLICY_CATEGORY_DESCRIPTION_REQUIRED]: Parameters.SetPolicyCategoryDescriptionRequiredParams;
-    [WRITE_COMMANDS.SET_POLICY_CATEGORY_ATTENDEES_REQUIRED]: Parameters.SetPolicyCategoryAttendeesRequiredParams;
     [WRITE_COMMANDS.SET_WORKSPACE_CATEGORY_DESCRIPTION_HINT]: Parameters.SetWorkspaceCategoryDescriptionHintParams;
     [WRITE_COMMANDS.SET_POLICY_CATEGORY_RECEIPTS_REQUIRED]: Parameters.SetPolicyCategoryReceiptsRequiredParams;
     [WRITE_COMMANDS.REMOVE_POLICY_CATEGORY_RECEIPTS_REQUIRED]: Parameters.RemovePolicyCategoryReceiptsRequiredParams;
diff --git a/src/libs/AttendeeUtils.ts b/src/libs/AttendeeUtils.ts
deleted file mode 100644
index a3ada3de4fe4..000000000000
--- a/src/libs/AttendeeUtils.ts
+++ /dev/null
@@ -1,92 +0,0 @@
-import type {LocaleContextProps} from '@components/LocaleContextProvider';
-import CONST from '@src/CONST';
-import type {PolicyCategories, PolicyCategory} from '@src/types/onyx';
-import type {Attendee} from '@src/types/onyx/IOU';
-import type {CurrentUserPersonalDetails} from '@src/types/onyx/PersonalDetails';
-
-/** Formats the title for requiredFields menu item based on which fields are enabled in the policy category */
-function formatRequiredFieldsTitle(translate: LocaleContextProps['translate'], policyCategory: PolicyCategory, isAttendeeTrackingEnabled = false): string {
-    const enabledFields: string[] = [];
-
-    // Attendees field should show first when both are selected and attendee tracking is enabled
-    if (isAttendeeTrackingEnabled && policyCategory.areAttendeesRequired) {
-        enabledFields.push(translate('iou.attendees'));
-    }
-
-    if (policyCategory.areCommentsRequired) {
-        enabledFields.push(translate('common.description'));
-    }
-
-    if (enabledFields.length === 0) {
-        return '';
-    }
-
-    const [first, ...rest] = enabledFields;
-    const capitalizedFirst = first.charAt(0).toUpperCase() + first.slice(1);
-    const lowercasedRest = rest.map((field) => field.charAt(0).toLowerCase() + field.slice(1));
-    return [capitalizedFirst, ...lowercasedRest].join(', ');
-}
-
-/** Returns whether there are missing attendees for the given category */
-function getIsMissingAttendeesViolation(
-    policyCategories: PolicyCategories | undefined,
-    category: string,
-    iouAttendees: Attendee[] | string | undefined,
-    userPersonalDetails: CurrentUserPersonalDetails,
-    isAttendeeTrackingEnabled = false,
-) {
-    const areAttendeesRequired = !!policyCategories?.[category ?? '']?.areAttendeesRequired;
-    // If attendee tracking is disabled at the policy level, don't enforce attendee requirement
-    if (!isAttendeeTrackingEnabled || !areAttendeesRequired) {
-        return false;
-    }
-
-    const creatorLogin = userPersonalDetails.login ?? '';
-    const attendees = Array.isArray(iouAttendees) ? iouAttendees : [];
-    const attendeesMinusCreatorCount = attendees.filter((a) => a?.login !== creatorLogin).length;
-
-    if (attendees.length === 0 || attendeesMinusCreatorCount === 0) {
-        return true;
-    }
-
-    return false;
-}
-
-/**
- * Syncs the missingAttendees violation with current policy settings.
- * - Adds the violation when it should show but isn't present from BE
- * - Removes stale BE violation when policy settings changed (e.g., category no longer requires attendees)
- */
-function syncMissingAttendeesViolation<T extends {name: string}>(
-    violations: T[],
-    policyCategories: PolicyCategories | undefined,
-    category: string,
-    attendees: Attendee[] | undefined,
-    userPersonalDetails: CurrentUserPersonalDetails,
-    isAttendeeTrackingEnabled: boolean,
-    isControlPolicy: boolean,
-): T[] {
-    const hasMissingAttendeesViolation = violations.some((v) => v.name === CONST.VIOLATIONS.MISSING_ATTENDEES);
-    const shouldShowMissingAttendees =
-        isControlPolicy && getIsMissingAttendeesViolation(policyCategories ?? {}, category ?? '', attendees ?? [], userPersonalDetails, isAttendeeTrackingEnabled);
-
-    if (!hasMissingAttendeesViolation && shouldShowMissingAttendees) {
-        // Add violation when it should show but isn't present from BE
-        return [
-            ...violations,
-            {
-                name: CONST.VIOLATIONS.MISSING_ATTENDEES,
-                type: CONST.VIOLATION_TYPES.VIOLATION,
-                showInReview: true,
-            } as unknown as T,
-        ];
-    }
-    if (hasMissingAttendeesViolation && !shouldShowMissingAttendees) {
-        // Remove stale BE violation when policy settings changed
-        return violations.filter((v) => v.name !== CONST.VIOLATIONS.MISSING_ATTENDEES);
-    }
-
-    return violations;
-}
-
-export {formatRequiredFieldsTitle, getIsMissingAttendeesViolation, syncMissingAttendeesViolation};
diff --git a/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx b/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx
index 5b6efd0a59ac..59e3251ff905 100644
--- a/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx
+++ b/src/libs/Navigation/AppNavigator/ModalStackNavigators/index.tsx
@@ -430,7 +430,6 @@ const SettingsModalStackNavigator = createModalStackNavigator<SettingsNavigatorP
     [SCREENS.WORKSPACE.SHARE]: () => require<ReactComponentModule>('../../../../pages/workspace/WorkspaceOverviewSharePage').default,
     [SCREENS.WORKSPACE.CURRENCY]: () => require<ReactComponentModule>('../../../../pages/workspace/WorkspaceOverviewCurrencyPage').default,
     [SCREENS.WORKSPACE.CATEGORY_SETTINGS]: () => require<ReactComponentModule>('../../../../pages/workspace/categories/CategorySettingsPage').default,
-    [SCREENS.WORKSPACE.CATEGORY_REQUIRED_FIELDS]: () => require<ReactComponentModule>('../../../../pages/workspace/categories/CategoryRequiredFieldsPage').default,
     [SCREENS.WORKSPACE.ADDRESS]: () => require<ReactComponentModule>('../../../../pages/workspace/WorkspaceOverviewAddressPage').default,
     [SCREENS.WORKSPACE.PLAN]: () => require<ReactComponentModule>('../../../../pages/workspace/WorkspaceOverviewPlanTypePage').default,
     [SCREENS.WORKSPACE.CATEGORIES_SETTINGS]: () => require<ReactComponentModule>('../../../../pages/workspace/categories/WorkspaceCategoriesSettingsPage').default,
diff --git a/src/libs/Navigation/linkingConfig/RELATIONS/WORKSPACE_TO_RHP.ts b/src/libs/Navigation/linkingConfig/RELATIONS/WORKSPACE_TO_RHP.ts
index e762868d3488..82742ec57e4f 100755
--- a/src/libs/Navigation/linkingConfig/RELATIONS/WORKSPACE_TO_RHP.ts
+++ b/src/libs/Navigation/linkingConfig/RELATIONS/WORKSPACE_TO_RHP.ts
@@ -213,7 +213,6 @@ const WORKSPACE_TO_RHP: Partial<Record<keyof WorkspaceSplitNavigatorParamList, s
         SCREENS.WORKSPACE.CATEGORY_DESCRIPTION_HINT,
         SCREENS.WORKSPACE.CATEGORY_APPROVER,
         SCREENS.WORKSPACE.CATEGORY_REQUIRE_RECEIPTS_OVER,
-        SCREENS.WORKSPACE.CATEGORY_REQUIRED_FIELDS,
     ],
     [SCREENS.WORKSPACE.DISTANCE_RATES]: [
         SCREENS.WORKSPACE.DISTANCE_RATES_SETTINGS,
diff --git a/src/libs/Navigation/linkingConfig/config.ts b/src/libs/Navigation/linkingConfig/config.ts
index f430fc340f7a..4a74d8a1f0df 100644
--- a/src/libs/Navigation/linkingConfig/config.ts
+++ b/src/libs/Navigation/linkingConfig/config.ts
@@ -921,9 +921,6 @@ const config: LinkingOptions<RootNavigatorParamList>['config'] = {
                         [SCREENS.WORKSPACE.CATEGORY_REQUIRE_RECEIPTS_OVER]: {
                             path: ROUTES.WORKSPACE_CATEGORY_REQUIRE_RECEIPTS_OVER.route,
                         },
-                        [SCREENS.WORKSPACE.CATEGORY_REQUIRED_FIELDS]: {
-                            path: ROUTES.WORKSPACE_CATEGORY_REQUIRED_FIELDS.route,
-                        },
                         [SCREENS.WORKSPACE.CREATE_DISTANCE_RATE]: {
                             path: ROUTES.WORKSPACE_CREATE_DISTANCE_RATE.route,
                         },
diff --git a/src/libs/Navigation/types.ts b/src/libs/Navigation/types.ts
index 0aa2c14b6735..19aee4008e02 100644
--- a/src/libs/Navigation/types.ts
+++ b/src/libs/Navigation/types.ts
@@ -335,10 +335,6 @@ type SettingsNavigatorParamList = {
         // eslint-disable-next-line no-restricted-syntax -- `backTo` usages in this file are legacy. Do not add new `backTo` params to screens. See contributingGuides/NAVIGATION.md
         backTo?: Routes;
     };
-    [SCREENS.WORKSPACE.CATEGORY_REQUIRED_FIELDS]: {
-        policyID: string;
-        categoryName: string;
-    };
     [SCREENS.WORKSPACE.UPGRADE]: {
         policyID?: string;
         featureName?: string;
diff --git a/src/libs/TransactionUtils/index.ts b/src/libs/TransactionUtils/index.ts
index 0e333b1fdfbc..95a20b642728 100644
--- a/src/libs/TransactionUtils/index.ts
+++ b/src/libs/TransactionUtils/index.ts
@@ -462,7 +462,7 @@ function shouldShowAttendees(iouType: IOUType, policy: OnyxEntry<Policy>): boole
 
     // For backwards compatibility with Expensify Classic, we assume that Attendee Tracking is enabled by default on
     // Control policies if the policy does not contain the attribute
-    return policy?.isAttendeeTrackingEnabled ?? false;
+    return policy?.isAttendeeTrackingEnabled ?? true;
 }
 
 /**
@@ -1443,7 +1443,6 @@ function shouldShowViolation(
     const isSubmitter = isCurrentUserSubmitter(iouReport);
     const isPolicyMember = isPolicyMemberPolicyUtils(policy, currentUserEmail);
     const isReportOpen = isOpenExpenseReport(iouReport);
-    const isAttendeeTrackingEnabled = policy?.isAttendeeTrackingEnabled ?? false;
 
     if (violationName === CONST.VIOLATIONS.AUTO_REPORTED_REJECTED_EXPENSE) {
         return isSubmitter || isPolicyAdmin(policy);
@@ -1461,10 +1460,6 @@ function shouldShowViolation(
         return isPolicyMember && !isSubmitter && !isReportOpen;
     }
 
-    if (violationName === CONST.VIOLATIONS.MISSING_ATTENDEES) {
-        return isAttendeeTrackingEnabled;
-    }
-
     if (violationName === CONST.VIOLATIONS.MISSING_CATEGORY && isCategoryBeingAnalyzed(transaction)) {
         return false;
     }
diff --git a/src/libs/Violations/ViolationsUtils.ts b/src/libs/Violations/ViolationsUtils.ts
index 5967c56828a7..77a1151fa3aa 100644
--- a/src/libs/Violations/ViolationsUtils.ts
+++ b/src/libs/Violations/ViolationsUtils.ts
@@ -4,7 +4,6 @@ import reject from 'lodash/reject';
 import type {OnyxCollection, OnyxEntry, OnyxUpdate} from 'react-native-onyx';
 import Onyx from 'react-native-onyx';
 import type {LocaleContextProps} from '@components/LocaleContextProvider';
-import {getCurrentUserEmail} from '@libs/actions/Report';
 import {getDecodedCategoryName, isCategoryMissing} from '@libs/CategoryUtils';
 import * as CurrencyUtils from '@libs/CurrencyUtils';
 import DateUtils from '@libs/DateUtils';
@@ -336,7 +335,6 @@ const ViolationsUtils = {
         // const hasOverTripLimitViolation = transactionViolations.some((violation) => violation.name === CONST.VIOLATIONS.OVER_TRIP_LIMIT);
         const hasCategoryOverLimitViolation = transactionViolations.some((violation) => violation.name === CONST.VIOLATIONS.OVER_CATEGORY_LIMIT);
         const hasMissingCommentViolation = transactionViolations.some((violation) => violation.name === CONST.VIOLATIONS.MISSING_COMMENT);
-        const hasMissingAttendeesViolation = transactionViolations.some((violation) => violation.name === CONST.VIOLATIONS.MISSING_ATTENDEES);
         const hasTaxOutOfPolicyViolation = transactionViolations.some((violation) => violation.name === CONST.VIOLATIONS.TAX_OUT_OF_POLICY);
         const isDistanceRequest = TransactionUtils.isDistanceRequest(updatedTransaction);
         const isPerDiemRequest = TransactionUtils.isPerDiemRequest(updatedTransaction);
@@ -384,40 +382,6 @@ const ViolationsUtils = {
         const shouldCategoryShowOverLimitViolation =
             canCalculateAmountViolations && !isInvoiceTransaction && typeof categoryOverLimit === 'number' && expenseAmount > categoryOverLimit && isControlPolicy;
         const shouldShowMissingComment = !isInvoiceTransaction && policyCategories?.[categoryName ?? '']?.areCommentsRequired && !updatedTransaction.comment?.comment && isControlPolicy;
-        const attendees = updatedTransaction.modifiedAttendees ?? updatedTransaction.comment?.attendees ?? [];
-        const isAttendeeTrackingEnabled = policy.isAttendeeTrackingEnabled ?? false;
-        // Filter out the owner/creator when checking attendance count - expense is valid if at least one non-owner attendee is present
-        const ownerAccountID = iouReport?.ownerAccountID;
-        // Calculate attendees minus owner. When ownerAccountID is known, filter by accountID.
-        // When ownerAccountID is undefined (offline split where iouReport is unavailable),
-        // fallback to using login/email to identify the owner (similar to AttendeeUtils approach).
-        let attendeesMinusOwnerCount: number;
-        if (ownerAccountID !== undefined) {
-            // Normal case: filter by accountID
-            attendeesMinusOwnerCount = attendees.filter((a) => a?.accountID !== ownerAccountID).length;
-        } else {
-            // Offline scenario: ownerAccountID unavailable, use login/email as fallback
-            const currentUserEmail = getCurrentUserEmail();
-            if (currentUserEmail) {
-                // Filter by login or email to identify owner
-                attendeesMinusOwnerCount = attendees.filter((a) => {
-                    const attendeeIdentifier = a?.login ?? a?.email;
-                    return attendeeIdentifier !== currentUserEmail;
-                }).length;
-            } else {
-                // Can't identify owner at all - if there are attendees, assume owner is one of them
-                // This means we need at least 2 attendees to have a non-owner attendee
-                attendeesMinusOwnerCount = Math.max(0, attendees.length - 1);
-            }
-        }
-
-        const shouldShowMissingAttendees =
-            !isInvoiceTransaction &&
-            isAttendeeTrackingEnabled &&
-            policyCategories?.[categoryName ?? '']?.areAttendeesRequired &&
-            isControlPolicy &&
-            (attendees.length === 0 || attendeesMinusOwnerCount === 0);
-
         const hasFutureDateViolation = transactionViolations.some((violation) => violation.name === 'futureDate');
         // Add 'futureDate' violation if transaction date is in the future and policy type is corporate
         if (!hasFutureDateViolation && shouldDisplayFutureDateViolation) {
@@ -500,18 +464,6 @@ const ViolationsUtils = {
             newTransactionViolations = reject(newTransactionViolations, {name: CONST.VIOLATIONS.MISSING_COMMENT});
         }
 
-        if (!hasMissingAttendeesViolation && shouldShowMissingAttendees) {
-            newTransactionViolations.push({
-                name: CONST.VIOLATIONS.MISSING_ATTENDEES,
-                type: CONST.VIOLATION_TYPES.VIOLATION,
-                showInReview: true,
-            });
-        }
-
-        if (hasMissingAttendeesViolation && !shouldShowMissingAttendees) {
-            newTransactionViolations = reject(newTransactionViolations, {name: CONST.VIOLATIONS.MISSING_ATTENDEES});
-        }
-
         if (isPolicyTrackTaxEnabled && !hasTaxOutOfPolicyViolation && !isTaxInPolicy) {
             newTransactionViolations.push({name: CONST.VIOLATIONS.TAX_OUT_OF_POLICY, type: CONST.VIOLATION_TYPES.VIOLATION, showInReview: true});
         }
@@ -581,8 +533,6 @@ const ViolationsUtils = {
                 return translate('violations.missingCategory');
             case 'missingComment':
                 return translate('violations.missingComment');
-            case 'missingAttendees':
-                return translate('violations.missingAttendees');
             case 'missingTag':
                 return translate('violations.missingTag', {tagName});
             case 'modifiedAmount':
diff --git a/src/libs/actions/IOU/index.ts b/src/libs/actions/IOU/index.ts
index 0a95d5e19da5..7b7f72e34adc 100644
--- a/src/libs/actions/IOU/index.ts
+++ b/src/libs/actions/IOU/index.ts
@@ -4306,7 +4306,6 @@ function getUpdateMoneyRequestParams(params: GetUpdateMoneyRequestParamsType): U
     const hasModifiedTaxCode = 'taxCode' in transactionChanges;
     const hasModifiedDate = 'date' in transactionChanges;
     const hasModifiedMerchant = 'merchant' in transactionChanges;
-    const hasModifiedAttendees = 'attendees' in transactionChanges;
 
     const isInvoice = isInvoiceReportReportUtils(iouReport);
     if (
@@ -4325,8 +4324,7 @@ function getUpdateMoneyRequestParams(params: GetUpdateMoneyRequestParamsType): U
             hasModifiedAmount ||
             hasModifiedCreated ||
             hasModifiedReimbursable ||
-            hasModifiedTaxCode ||
-            hasModifiedAttendees)
+            hasModifiedTaxCode)
     ) {
         const currentTransactionViolations = allTransactionViolations[`${ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS}${transactionID}`] ?? [];
         // If the amount, currency or date have been modified, we remove the duplicate violations since they would be out of date as the transaction has changed
diff --git a/src/libs/actions/Policy/Category.ts b/src/libs/actions/Policy/Category.ts
index 27b700b2970c..a404d2854d4a 100644
--- a/src/libs/actions/Policy/Category.ts
+++ b/src/libs/actions/Policy/Category.ts
@@ -11,7 +11,6 @@ import type {
     OpenPolicyCategoriesPageParams,
     RemovePolicyCategoryReceiptsRequiredParams,
     SetPolicyCategoryApproverParams,
-    SetPolicyCategoryAttendeesRequiredParams,
     SetPolicyCategoryDescriptionRequiredParams,
     SetPolicyCategoryMaxAmountParams,
     SetPolicyCategoryReceiptsRequiredParams,
@@ -1521,68 +1520,6 @@ function setPolicyCategoryTax(policy: OnyxEntry<Policy>, categoryName: string, t
     API.write(WRITE_COMMANDS.SET_POLICY_CATEGORY_TAX, parameters, onyxData);
 }
 
-function setPolicyCategoryAttendeesRequired(policyID: string, categoryName: string, areAttendeesRequired: boolean, policyCategories: PolicyCategories = {}) {
-    const policyCategoryToUpdate = policyCategories?.[categoryName];
-    const originalAreAttendeesRequired = policyCategoryToUpdate?.areAttendeesRequired;
-
-    const onyxData: OnyxData = {
-        optimisticData: [
-            {
-                onyxMethod: Onyx.METHOD.MERGE,
-                key: `${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${policyID}`,
-                value: {
-                    [categoryName]: {
-                        pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE,
-                        pendingFields: {
-                            areAttendeesRequired: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE,
-                        },
-                        areAttendeesRequired,
-                    },
-                },
-            },
-        ],
-        successData: [
-            {
-                onyxMethod: Onyx.METHOD.MERGE,
-                key: `${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${policyID}`,
-                value: {
-                    [categoryName]: {
-                        pendingAction: null,
-                        pendingFields: {
-                            areAttendeesRequired: null,
-                        },
-                        areAttendeesRequired,
-                    },
-                },
-            },
-        ],
-        failureData: [
-            {
-                onyxMethod: Onyx.METHOD.MERGE,
-                key: `${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${policyID}`,
-                value: {
-                    [categoryName]: {
-                        errors: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('common.genericErrorMessage'),
-                        pendingAction: null,
-                        pendingFields: {
-                            areAttendeesRequired: null,
-                        },
-                        areAttendeesRequired: originalAreAttendeesRequired,
-                    },
-                },
-            },
-        ],
-    };
-
-    const parameters: SetPolicyCategoryAttendeesRequiredParams = {
-        policyID,
-        categoryName,
-        areAttendeesRequired,
-    };
-
-    API.write(WRITE_COMMANDS.SET_POLICY_CATEGORY_ATTENDEES_REQUIRED, parameters, onyxData);
-}
-
 export {
     buildOptimisticPolicyCategories,
     buildOptimisticMccGroup,
@@ -1597,7 +1534,6 @@ export {
     removePolicyCategoryReceiptsRequired,
     renamePolicyCategory,
     setPolicyCategoryApprover,
-    setPolicyCategoryAttendeesRequired,
     setPolicyCategoryDescriptionRequired,
     buildOptimisticPolicyWithExistingCategories,
     setPolicyCategoryGLCode,
diff --git a/src/pages/iou/request/step/IOURequestStepAttendees.tsx b/src/pages/iou/request/step/IOURequestStepAttendees.tsx
index 5258f40e7b89..e2f4c024fb57 100644
--- a/src/pages/iou/request/step/IOURequestStepAttendees.tsx
+++ b/src/pages/iou/request/step/IOURequestStepAttendees.tsx
@@ -1,5 +1,6 @@
 import {deepEqual} from 'fast-equals';
 import React, {useCallback, useState} from 'react';
+import type {OnyxEntry} from 'react-native-onyx';
 import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails';
 import useLocalize from '@hooks/useLocalize';
 import useOnyx from '@hooks/useOnyx';
@@ -14,27 +15,37 @@ import MoneyRequestAttendeeSelector from '@pages/iou/request/MoneyRequestAttende
 import CONST from '@src/CONST';
 import ONYXKEYS from '@src/ONYXKEYS';
 import type SCREENS from '@src/SCREENS';
+import type * as OnyxTypes from '@src/types/onyx';
 import type {Attendee} from '@src/types/onyx/IOU';
 import StepScreenWrapper from './StepScreenWrapper';
 import type {WithWritableReportOrNotFoundProps} from './withWritableReportOrNotFound';
 import withWritableReportOrNotFound from './withWritableReportOrNotFound';
 
-type IOURequestStepAttendeesProps = WithWritableReportOrNotFoundProps<typeof SCREENS.MONEY_REQUEST.STEP_ATTENDEES>;
+type IOURequestStepAttendeesOnyxProps = {
+    /** The policy of the report */
+    policy: OnyxEntry<OnyxTypes.Policy>;
+
+    /** Collection of categories attached to a policy */
+    policyCategories: OnyxEntry<OnyxTypes.PolicyCategories>;
+
+    /** Collection of tags attached to a policy */
+    policyTags: OnyxEntry<OnyxTypes.PolicyTagLists>;
+};
+
+type IOURequestStepAttendeesProps = IOURequestStepAttendeesOnyxProps & WithWritableReportOrNotFoundProps<typeof SCREENS.MONEY_REQUEST.STEP_ATTENDEES>;
 
 function IOURequestStepAttendees({
     route: {
         params: {transactionID, reportID, iouType, backTo, action},
     },
+    policy,
+    policyTags,
+    policyCategories,
 }: IOURequestStepAttendeesProps) {
     const currentUserPersonalDetails = useCurrentUserPersonalDetails();
     const isEditing = action === CONST.IOU.ACTION.EDIT;
     // eslint-disable-next-line rulesdir/no-default-id-values
     const [transaction] = useOnyx(`${isEditing ? ONYXKEYS.COLLECTION.TRANSACTION : ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${transactionID || CONST.DEFAULT_NUMBER_ID}`, {canBeMissing: true});
-    const [report] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${reportID}`, {canBeMissing: true});
-    const policyID = report?.policyID;
-    const [policy] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`, {canBeMissing: true});
-    const [policyCategories] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${policyID}`, {canBeMissing: true});
-    const [policyTags] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY_TAGS}${policyID}`, {canBeMissing: true});
     const [attendees, setAttendees] = useState<Attendee[]>(() => getOriginalAttendees(transaction, currentUserPersonalDetails));
     const previousAttendees = usePrevious(attendees);
     const {translate} = useLocalize();
diff --git a/src/pages/workspace/categories/CategoryRequiredFieldsPage.tsx b/src/pages/workspace/categories/CategoryRequiredFieldsPage.tsx
deleted file mode 100644
index 728e9792dd38..000000000000
--- a/src/pages/workspace/categories/CategoryRequiredFieldsPage.tsx
+++ /dev/null
@@ -1,103 +0,0 @@
-import React from 'react';
-import {View} from 'react-native';
-import HeaderWithBackButton from '@components/HeaderWithBackButton';
-import OfflineWithFeedback from '@components/OfflineWithFeedback';
-import RenderHTML from '@components/RenderHTML';
-import ScreenWrapper from '@components/ScreenWrapper';
-import ScrollView from '@components/ScrollView';
-import Switch from '@components/Switch';
-import Text from '@components/Text';
-import useLocalize from '@hooks/useLocalize';
-import useOnyx from '@hooks/useOnyx';
-import useThemeStyles from '@hooks/useThemeStyles';
-import {getDecodedCategoryName} from '@libs/CategoryUtils';
-import Navigation from '@libs/Navigation/Navigation';
-import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types';
-import type {SettingsNavigatorParamList} from '@navigation/types';
-import AccessOrNotFoundWrapper from '@pages/workspace/AccessOrNotFoundWrapper';
-import {setPolicyCategoryAttendeesRequired, setPolicyCategoryDescriptionRequired} from '@userActions/Policy/Category';
-import CONST from '@src/CONST';
-import ONYXKEYS from '@src/ONYXKEYS';
-import ROUTES from '@src/ROUTES';
-import type SCREENS from '@src/SCREENS';
-
-type CategoryRequiredFieldsPageProps = PlatformStackScreenProps<SettingsNavigatorParamList, typeof SCREENS.WORKSPACE.CATEGORY_REQUIRED_FIELDS>;
-
-function CategoryRequiredFieldsPage({
-    route: {
-        params: {policyID, categoryName},
-    },
-}: CategoryRequiredFieldsPageProps) {
-    const styles = useThemeStyles();
-    const {translate} = useLocalize();
-    const [policyCategories] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${policyID}`, {canBeMissing: true});
-    const [policy] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`, {canBeMissing: true});
-    const decodedCategoryName = getDecodedCategoryName(categoryName);
-
-    const policyCategory = policyCategories?.[categoryName];
-    const areCommentsRequired = policyCategory?.areCommentsRequired ?? false;
-    const areAttendeesRequired = policyCategory?.areAttendeesRequired ?? false;
-    const isAttendeeTrackingEnabled = policy?.isAttendeeTrackingEnabled ?? false;
-
-    return (
-        <AccessOrNotFoundWrapper
-            accessVariants={[CONST.POLICY.ACCESS_VARIANTS.ADMIN, CONST.POLICY.ACCESS_VARIANTS.CONTROL]}
-            policyID={policyID}
-            featureName={CONST.POLICY.MORE_FEATURES.ARE_CATEGORIES_ENABLED}
-        >
-            <ScreenWrapper
-                enableEdgeToEdgeBottomSafeAreaPadding
-                style={[styles.defaultModalContainer]}
-                testID={CategoryRequiredFieldsPage.displayName}
-                shouldEnableMaxHeight
-            >
-                <HeaderWithBackButton
-                    title={translate('workspace.rules.categoryRules.requiredFieldsTitle')}
-                    onBackButtonPress={() => Navigation.goBack(ROUTES.WORKSPACE_CATEGORY_SETTINGS.getRoute(policyID, categoryName))}
-                />
-                <ScrollView
-                    contentContainerStyle={[styles.flexGrow1]}
-                    addBottomSafeAreaPadding
-                >
-                    <View style={[styles.mh5, styles.pb5]}>
-                        <RenderHTML html={translate('workspace.rules.categoryRules.requiredFieldsDescription', decodedCategoryName)} />
-                    </View>
-                    <OfflineWithFeedback pendingAction={policyCategory?.pendingFields?.areCommentsRequired}>
-                        <View style={[styles.mt2, styles.mh5]}>
-                            <View style={[styles.flexRow, styles.mb5, styles.mr2, styles.alignItemsCenter, styles.justifyContentBetween]}>
-                                <Text style={[styles.flexShrink1, styles.mr2]}>{translate('workspace.rules.categoryRules.requireDescription')}</Text>
-                                <Switch
-                                    isOn={areCommentsRequired}
-                                    accessibilityLabel={translate('workspace.rules.categoryRules.requireDescription')}
-                                    onToggle={() => {
-                                        setPolicyCategoryDescriptionRequired(policyID, categoryName, !areCommentsRequired, policyCategories);
-                                    }}
-                                />
-                            </View>
-                        </View>
-                    </OfflineWithFeedback>
-                    {isAttendeeTrackingEnabled && (
-                        <OfflineWithFeedback pendingAction={policyCategory?.pendingFields?.areAttendeesRequired}>
-                            <View style={[styles.mh5]}>
-                                <View style={[styles.flexRow, styles.mv5, styles.mr2, styles.alignItemsCenter, styles.justifyContentBetween]}>
-                                    <Text style={[styles.flexShrink1, styles.mr2]}>{translate('workspace.rules.categoryRules.requireAttendees')}</Text>
-                                    <Switch
-                                        isOn={areAttendeesRequired}
-                                        accessibilityLabel={translate('workspace.rules.categoryRules.requireAttendees')}
-                                        onToggle={() => {
-                                            setPolicyCategoryAttendeesRequired(policyID, categoryName, !areAttendeesRequired, policyCategories);
-                                        }}
-                                    />
-                                </View>
-                            </View>
-                        </OfflineWithFeedback>
-                    )}
-                </ScrollView>
-            </ScreenWrapper>
-        </AccessOrNotFoundWrapper>
-    );
-}
-
-CategoryRequiredFieldsPage.displayName = 'CategoryRequiredFieldsPage';
-
-export default CategoryRequiredFieldsPage;
diff --git a/src/pages/workspace/categories/CategorySettingsPage.tsx b/src/pages/workspace/categories/CategorySettingsPage.tsx
index 1eb16f3d9977..e22c8bc46aa1 100644
--- a/src/pages/workspace/categories/CategorySettingsPage.tsx
+++ b/src/pages/workspace/categories/CategorySettingsPage.tsx
@@ -17,7 +17,6 @@ import useLocalize from '@hooks/useLocalize';
 import useOnboardingTaskInformation from '@hooks/useOnboardingTaskInformation';
 import usePolicyData from '@hooks/usePolicyData';
 import useThemeStyles from '@hooks/useThemeStyles';
-import {formatRequiredFieldsTitle} from '@libs/AttendeeUtils';
 import {formatDefaultTaxRateText, formatRequireReceiptsOverText, getCategoryApproverRule, getCategoryDefaultTaxRate, getDecodedCategoryName} from '@libs/CategoryUtils';
 import {convertToDisplayString} from '@libs/CurrencyUtils';
 import {getLatestErrorMessageField} from '@libs/ErrorUtils';
@@ -29,7 +28,13 @@ import {getWorkflowApprovalsUnavailable, isControlPolicy} from '@libs/PolicyUtil
 import type {SettingsNavigatorParamList} from '@navigation/types';
 import NotFoundPage from '@pages/ErrorPage/NotFoundPage';
 import AccessOrNotFoundWrapper from '@pages/workspace/AccessOrNotFoundWrapper';
-import {clearCategoryErrors, deleteWorkspaceCategories, setWorkspaceCategoryEnabled} from '@userActions/Policy/Category';
+import {
+    clearCategoryErrors,
+    deleteWorkspaceCategories,
+    setPolicyCategoryDescriptionRequired,
+    setWorkspaceCategoryDescriptionHint,
+    setWorkspaceCategoryEnabled,
+} from '@userActions/Policy/Category';
 import CONST from '@src/CONST';
 import ROUTES from '@src/ROUTES';
 import SCREENS from '@src/SCREENS';
@@ -122,22 +127,6 @@ function CategorySettingsPage({
         return formatRequireReceiptsOverText(translate, policy, policyCategory?.maxAmountNoReceipt);
     }, [policy, policyCategory?.maxAmountNoReceipt, translate]);
 
-    const requiredFieldsTitle = useMemo(() => {
-        if (!policyCategory) {
-            return '';
-        }
-        return formatRequiredFieldsTitle(translate, policyCategory, policy?.isAttendeeTrackingEnabled);
-    }, [policyCategory, translate, policy?.isAttendeeTrackingEnabled]);
-
-    const requireFieldsPendingAction = useMemo(() => {
-        if (policy?.isAttendeeTrackingEnabled) {
-            // Pending fields are objects so we can't use nullish coalescing
-            // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
-            return policyCategory?.pendingFields?.areAttendeesRequired || policyCategory?.pendingFields?.areCommentsRequired;
-        }
-        return policyCategory?.pendingFields?.areCommentsRequired;
-    }, [policyCategory?.pendingFields, policy?.isAttendeeTrackingEnabled]);
-
     if (!policyCategory) {
         return <NotFoundPage />;
     }
@@ -299,37 +288,40 @@ function CategorySettingsPage({
                         />
                     </OfflineWithFeedback>
 
-                    {areCommentsRequired && (
-                        <OfflineWithFeedback pendingAction={policyCategory.pendingFields?.commentHint}>
-                            <MenuItemWithTopDescription
-                                title={policyCategory?.commentHint}
-                                description={translate('workspace.rules.categoryRules.descriptionHint')}
-                                onPress={() => {
-                                    Navigation.navigate(ROUTES.WORKSPACE_CATEGORY_DESCRIPTION_HINT.getRoute(policyID, policyCategory.name));
-                                }}
-                                shouldShowRightIcon
-                            />
-                        </OfflineWithFeedback>
-                    )}
-                    {!isThereAnyAccountingConnection && (
-                        <MenuItem
-                            icon={Trashcan}
-                            title={translate('workspace.categories.deleteCategory')}
-                            onPress={() => {
-                                if (shouldPreventDisableOrDelete) {
-                                    setIsCannotDeleteOrDisableLastCategoryModalVisible(true);
-                                    return;
-                                }
-                                setDeleteCategoryConfirmModalVisible(true);
-                            }}
-                        />
-                    )}
-
                     {!!policy?.areRulesEnabled && (
                         <>
                             <View style={[styles.mh5, styles.pt3, styles.borderTop]}>
                                 <Text style={[styles.textNormal, styles.textStrong, styles.mv3]}>{translate('workspace.rules.categoryRules.title')}</Text>
                             </View>
+                            <OfflineWithFeedback pendingAction={policyCategory?.pendingFields?.areCommentsRequired}>
+                                <View style={[styles.mt2, styles.mh5]}>
+                                    <View style={[styles.flexRow, styles.mb5, styles.mr2, styles.alignItemsCenter, styles.justifyContentBetween]}>
+                                        <Text style={[styles.flexShrink1, styles.mr2]}>{translate('workspace.rules.categoryRules.requireDescription')}</Text>
+                                        <Switch
+                                            isOn={policyCategory?.areCommentsRequired ?? false}
+                                            accessibilityLabel={translate('workspace.rules.categoryRules.requireDescription')}
+                                            onToggle={() => {
+                                                if (policyCategory.commentHint && areCommentsRequired) {
+                                                    setWorkspaceCategoryDescriptionHint(policyID, categoryName, '', policyCategories);
+                                                }
+                                                setPolicyCategoryDescriptionRequired(policyID, categoryName, !areCommentsRequired, policyCategories);
+                                            }}
+                                        />
+                                    </View>
+                                </View>
+                            </OfflineWithFeedback>
+                            {!!policyCategory?.areCommentsRequired && (
+                                <OfflineWithFeedback pendingAction={policyCategory.pendingFields?.commentHint}>
+                                    <MenuItemWithTopDescription
+                                        title={policyCategory?.commentHint}
+                                        description={translate('workspace.rules.categoryRules.descriptionHint')}
+                                        onPress={() => {
+                                            Navigation.navigate(ROUTES.WORKSPACE_CATEGORY_DESCRIPTION_HINT.getRoute(policyID, policyCategory.name));
+                                        }}
+                                        shouldShowRightIcon
+                                    />
+                                </OfflineWithFeedback>
+                            )}
                             <MenuItemWithTopDescription
                                 title={approverText}
                                 description={translate('workspace.rules.categoryRules.approver')}
@@ -378,18 +370,21 @@ function CategorySettingsPage({
                                     shouldShowRightIcon
                                 />
                             </OfflineWithFeedback>
-                            <OfflineWithFeedback pendingAction={requireFieldsPendingAction}>
-                                <MenuItemWithTopDescription
-                                    title={requiredFieldsTitle}
-                                    description={translate('workspace.rules.categoryRules.requireFields')}
-                                    onPress={() => {
-                                        Navigation.navigate(ROUTES.WORKSPACE_CATEGORY_REQUIRED_FIELDS.getRoute(policyID, policyCategory.name));
-                                    }}
-                                    shouldShowRightIcon
-                                />
-                            </OfflineWithFeedback>
                         </>
                     )}
+                    {!isThereAnyAccountingConnection && (
+                        <MenuItem
+                            icon={Trashcan}
+                            title={translate('common.delete')}
+                            onPress={() => {
+                                if (shouldPreventDisableOrDelete) {
+                                    setIsCannotDeleteOrDisableLastCategoryModalVisible(true);
+                                    return;
+                                }
+                                setDeleteCategoryConfirmModalVisible(true);
+                            }}
+                        />
+                    )}
                 </ScrollView>
             </ScreenWrapper>
         </AccessOrNotFoundWrapper>
diff --git a/src/pages/workspace/rules/IndividualExpenseRulesSection.tsx b/src/pages/workspace/rules/IndividualExpenseRulesSection.tsx
index 668e90f4d6cc..c79e070306e2 100644
--- a/src/pages/workspace/rules/IndividualExpenseRulesSection.tsx
+++ b/src/pages/workspace/rules/IndividualExpenseRulesSection.tsx
@@ -1,4 +1,4 @@
-import React, {useCallback, useMemo} from 'react';
+import React, {useMemo} from 'react';
 import {View} from 'react-native';
 import type {LocaleContextProps} from '@components/LocaleContextProvider';
 import MenuItemWithTopDescription from '@components/MenuItemWithTopDescription';
@@ -8,10 +8,8 @@ import Section from '@components/Section';
 import useCardFeeds from '@hooks/useCardFeeds';
 import useEnvironment from '@hooks/useEnvironment';
 import useLocalize from '@hooks/useLocalize';
-import useOnyx from '@hooks/useOnyx';
 import usePolicy from '@hooks/usePolicy';
 import useThemeStyles from '@hooks/useThemeStyles';
-import {setPolicyCategoryAttendeesRequired} from '@libs/actions/Policy/Category';
 import {getCashExpenseReimbursableMode, setPolicyAttendeeTrackingEnabled, setPolicyRequireCompanyCardsEnabled, setWorkspaceEReceiptsEnabled} from '@libs/actions/Policy/Policy';
 import {convertToDisplayString} from '@libs/CurrencyUtils';
 import Navigation from '@libs/Navigation/Navigation';
@@ -19,7 +17,6 @@ import ToggleSettingOptionRow from '@pages/workspace/workflows/ToggleSettingsOpt
 import type {ThemeStyles} from '@styles/index';
 import CONST from '@src/CONST';
 import type {TranslationPaths} from '@src/languages/types';
-import ONYXKEYS from '@src/ONYXKEYS';
 import ROUTES from '@src/ROUTES';
 import type {Policy} from '@src/types/onyx';
 import type {PendingAction} from '@src/types/onyx/OnyxCommon';
@@ -75,27 +72,9 @@ function IndividualExpenseRulesSection({policyID}: IndividualExpenseRulesSection
     const policy = usePolicy(policyID);
     const [cardFeeds] = useCardFeeds(policyID);
     const {environmentURL} = useEnvironment();
-    const [policyCategories] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${policyID}`, {canBeMissing: true});
 
     const policyCurrency = policy?.outputCurrency ?? CONST.CURRENCY.USD;
 
-    const handleAttendeeTrackingToggle = useCallback(
-        (newValue: boolean) => {
-            // When disabling attendee tracking, disable areAttendeesRequired on all categories
-            // that have it enabled in order to avoid BE validation errors
-            if (!newValue && policyCategories) {
-                for (const [categoryName, category] of Object.entries(policyCategories)) {
-                    if (!category?.areAttendeesRequired) {
-                        continue;
-                    }
-                    setPolicyCategoryAttendeesRequired(policyID, categoryName, false, policyCategories);
-                }
-            }
-            setPolicyAttendeeTrackingEnabled(policyID, newValue);
-        },
-        [policyID, policyCategories],
-    );
-
     const maxExpenseAmountNoReceiptText = useMemo(() => {
         if (policy?.maxExpenseAmountNoReceipt === CONST.DISABLED_MAX_EXPENSE_VALUE) {
             return '';
@@ -201,7 +180,7 @@ function IndividualExpenseRulesSection({policyID}: IndividualExpenseRulesSection
 
     // For backwards compatibility with Expensify Classic, we assume that Attendee Tracking is enabled by default on
     // Control policies if the policy does not contain the attribute
-    const isAttendeeTrackingEnabled = policy?.isAttendeeTrackingEnabled ?? false;
+    const isAttendeeTrackingEnabled = policy?.isAttendeeTrackingEnabled ?? true;
 
     return (
         <Section
@@ -270,7 +249,7 @@ function IndividualExpenseRulesSection({policyID}: IndividualExpenseRulesSection
                     titleStyle={styles.pv2}
                     subtitleStyle={styles.pt1}
                     isActive={isAttendeeTrackingEnabled}
-                    onToggle={() => handleAttendeeTrackingToggle(!isAttendeeTrackingEnabled)}
+                    onToggle={() => setPolicyAttendeeTrackingEnabled(policyID, !isAttendeeTrackingEnabled)}
                     pendingAction={policy?.pendingFields?.isAttendeeTrackingEnabled}
                 />
             </View>
diff --git a/src/types/onyx/PolicyCategory.ts b/src/types/onyx/PolicyCategory.ts
index 4f0848dde72f..90aa92ca9367 100644
--- a/src/types/onyx/PolicyCategory.ts
+++ b/src/types/onyx/PolicyCategory.ts
@@ -48,9 +48,6 @@ type PolicyCategory = OnyxCommon.OnyxValueWithOfflineFeedback<{
 
     /** Max expense amount with no receipt violation */
     maxAmountNoReceipt?: number | null;
-
-    /** If true, not providing attendees for a transaction with this category will trigger a violation */
-    areAttendeesRequired?: boolean;
 }>;
 
 /** Record of policy categories, indexed by their name */
diff --git a/tests/unit/ViolationUtilsTest.ts b/tests/unit/ViolationUtilsTest.ts
index cfa737877593..43e0798470c8 100644
--- a/tests/unit/ViolationUtilsTest.ts
+++ b/tests/unit/ViolationUtilsTest.ts
@@ -9,12 +9,6 @@ import type {Policy, PolicyCategories, PolicyTagLists, Report, Transaction, Tran
 import type {TransactionCollectionDataSet} from '@src/types/onyx/Transaction';
 import {translateLocal} from '../utils/TestHelper';
 
-// Mock getCurrentUserEmail from Report actions
-const MOCK_CURRENT_USER_EMAIL = 'test@expensify.com';
-jest.mock('@libs/actions/Report', () => ({
-    getCurrentUserEmail: jest.fn(() => MOCK_CURRENT_USER_EMAIL),
-}));
-
 const categoryOutOfPolicyViolation = {
     name: CONST.VIOLATIONS.CATEGORY_OUT_OF_POLICY,
     type: CONST.VIOLATION_TYPES.VIOLATION,
@@ -603,203 +597,6 @@ describe('getViolationsOnyxData', () => {
             expect(result.value).toEqual(expect.arrayContaining([missingDepartmentTag, missingRegionTag, missingProjectTag]));
         });
     });
-
-    describe('missingAttendees violation', () => {
-        const missingAttendeesViolation = {
-            name: CONST.VIOLATIONS.MISSING_ATTENDEES,
-            type: CONST.VIOLATION_TYPES.VIOLATION,
-            showInReview: true,
-        };
-
-        const ownerAccountID = 123;
-        const otherAccountID = 456;
-
-        let iouReport: Report;
-
-        beforeEach(() => {
-            policy.type = CONST.POLICY.TYPE.CORPORATE;
-            policy.isAttendeeTrackingEnabled = true;
-            policyCategories = {
-                Meals: {
-                    name: 'Meals',
-                    enabled: true,
-                    areAttendeesRequired: true,
-                },
-            };
-            transaction.category = 'Meals';
-            iouReport = {
-                reportID: '1234',
-                ownerAccountID,
-            } as Report;
-        });
-
-        it('should add missingAttendees violation when no attendees are present', () => {
-            transaction.comment = {attendees: []};
-            const result = ViolationsUtils.getViolationsOnyxData(transaction, transactionViolations, policy, policyTags, policyCategories, false, false, false, iouReport);
-            expect(result.value).toEqual(expect.arrayContaining([missingAttendeesViolation]));
-        });
-
-        it('should add missingAttendees violation when only owner is an attendee', () => {
-            transaction.comment = {
-                attendees: [{email: 'owner@example.com', displayName: 'Owner', avatarUrl: '', accountID: ownerAccountID}],
-            };
-            const result = ViolationsUtils.getViolationsOnyxData(transaction, transactionViolations, policy, policyTags, policyCategories, false, false, false, iouReport);
-            expect(result.value).toEqual(expect.arrayContaining([missingAttendeesViolation]));
-        });
-
-        it('should not add missingAttendees violation when there is at least one non-owner attendee', () => {
-            transaction.comment = {
-                attendees: [
-                    {email: 'owner@example.com', displayName: 'Owner', avatarUrl: '', accountID: ownerAccountID},
-                    {email: 'other@example.com', displayName: 'Other', avatarUrl: '', accountID: otherAccountID},
-                ],
-            };
-            const result = ViolationsUtils.getViolationsOnyxData(transaction, transactionViolations, policy, policyTags, policyCategories, false, false, false, iouReport);
-            expect(result.value).not.toEqual(expect.arrayContaining([missingAttendeesViolation]));
-        });
-
-        it('should remove missingAttendees violation when attendees are added', () => {
-            transactionViolations = [missingAttendeesViolation];
-            transaction.comment = {
-                attendees: [
-                    {email: 'owner@example.com', displayName: 'Owner', avatarUrl: '', accountID: ownerAccountID},
-                    {email: 'other@example.com', displayName: 'Other', avatarUrl: '', accountID: otherAccountID},
-                ],
-            };
-            const result = ViolationsUtils.getViolationsOnyxData(transaction, transactionViolations, policy, policyTags, policyCategories, false, false, false, iouReport);
-            expect(result.value).not.toEqual(expect.arrayContaining([missingAttendeesViolation]));
-        });
-
-        it('should not add missingAttendees violation when attendee tracking is disabled', () => {
-            policy.isAttendeeTrackingEnabled = false;
-            transaction.comment = {attendees: []};
-            const result = ViolationsUtils.getViolationsOnyxData(transaction, transactionViolations, policy, policyTags, policyCategories, false, false, false, iouReport);
-            expect(result.value).not.toEqual(expect.arrayContaining([missingAttendeesViolation]));
-        });
-
-        it('should not add missingAttendees violation when category does not require attendees', () => {
-            policyCategories.Meals.areAttendeesRequired = false;
-            transaction.comment = {attendees: []};
-            const result = ViolationsUtils.getViolationsOnyxData(transaction, transactionViolations, policy, policyTags, policyCategories, false, false, false, iouReport);
-            expect(result.value).not.toEqual(expect.arrayContaining([missingAttendeesViolation]));
-        });
-
-        describe('optimistic / offline scenarios (iouReport is undefined)', () => {
-            // In offline scenarios, iouReport is undefined so we can't get ownerAccountID.
-            // The code falls back to using getCurrentUserEmail() to identify the owner by login/email.
-            it('should correctly calculate violation when iouReport is undefined but attendees have matching email', () => {
-                // When iouReport is undefined, we use getCurrentUserEmail() as fallback
-                // If only the current user (matching MOCK_CURRENT_USER_EMAIL) is an attendee, violation should show
-                transactionViolations = [];
-                transaction.comment = {
-                    attendees: [{email: MOCK_CURRENT_USER_EMAIL, displayName: 'Test User', avatarUrl: ''}],
-                };
-                const result = ViolationsUtils.getViolationsOnyxData(transaction, transactionViolations, policy, policyTags, policyCategories, false, false, false, undefined);
-                // Violation should be added since the only attendee is the current user (owner)
-                expect(result.value).toEqual(expect.arrayContaining([missingAttendeesViolation]));
-            });
-
-            it('should not add violation when iouReport is undefined but there are non-owner attendees (by email)', () => {
-                // When there are attendees with different emails than the current user, no violation
-                transactionViolations = [];
-                transaction.comment = {
-                    attendees: [
-                        {email: MOCK_CURRENT_USER_EMAIL, displayName: 'Test User', avatarUrl: ''},
-                        {email: 'other@example.com', displayName: 'Other User', avatarUrl: ''},
-                    ],
-                };
-                const result = ViolationsUtils.getViolationsOnyxData(transaction, transactionViolations, policy, policyTags, policyCategories, false, false, false, undefined);
-                // Violation should NOT be added since there's a non-owner attendee
-                expect(result.value).not.toEqual(expect.arrayContaining([missingAttendeesViolation]));
-            });
-
-            it('should remove violation when non-owner attendee is added (offline)', () => {
-                // If violation existed and a non-owner attendee is added, violation should be removed
-                transactionViolations = [missingAttendeesViolation];
-                transaction.comment = {
-                    attendees: [
-                        {email: MOCK_CURRENT_USER_EMAIL, displayName: 'Test User', avatarUrl: ''},
-                        {email: 'other@example.com', displayName: 'Other User', avatarUrl: ''},
-                    ],
-                };
-                const result = ViolationsUtils.getViolationsOnyxData(transaction, transactionViolations, policy, policyTags, policyCategories, false, false, false, undefined);
-                // Violation should be removed
-                expect(result.value).not.toEqual(expect.arrayContaining([missingAttendeesViolation]));
-            });
-
-            it('should preserve violation when only owner attendee remains (offline)', () => {
-                // If violation existed and only owner attendee remains, violation stays
-                transactionViolations = [missingAttendeesViolation];
-                transaction.comment = {
-                    attendees: [{email: MOCK_CURRENT_USER_EMAIL, displayName: 'Test User', avatarUrl: ''}],
-                };
-                const result = ViolationsUtils.getViolationsOnyxData(transaction, transactionViolations, policy, policyTags, policyCategories, false, false, false, undefined);
-                // Violation should be preserved
-                expect(result.value).toEqual(expect.arrayContaining([missingAttendeesViolation]));
-            });
-        });
-
-        describe('fallback case (iouReport undefined AND getCurrentUserEmail returns falsy)', () => {
-            // This tests the edge case where we cannot identify the owner at all:
-            // - ownerAccountID is undefined (iouReport unavailable)
-            // - getCurrentUserEmail() returns falsy (no current user email)
-            // In this case, we assume owner is one of the attendees, so we need at least 2 attendees
-            // for there to be a non-owner attendee.
-
-            beforeEach(() => {
-                // Mock getCurrentUserEmail to return empty string
-                jest.spyOn(require('@libs/actions/Report'), 'getCurrentUserEmail').mockReturnValue('');
-            });
-
-            afterEach(() => {
-                jest.restoreAllMocks();
-            });
-
-            it("should add missingAttendees violation when no attendees are present (can't identify owner)", () => {
-                transactionViolations = [];
-                transaction.comment = {attendees: []};
-                const result = ViolationsUtils.getViolationsOnyxData(transaction, transactionViolations, policy, policyTags, policyCategories, false, false, false, undefined);
-                // With 0 attendees, attendeesMinusOwnerCount = Math.max(0, 0 - 1) = 0, violation should be added
-                expect(result.value).toEqual(expect.arrayContaining([missingAttendeesViolation]));
-            });
-
-            it('should add missingAttendees violation when only 1 attendee exists (assumed to be owner)', () => {
-                transactionViolations = [];
-                transaction.comment = {
-                    attendees: [{email: 'anyone@example.com', displayName: 'Someone', avatarUrl: ''}],
-                };
-                const result = ViolationsUtils.getViolationsOnyxData(transaction, transactionViolations, policy, policyTags, policyCategories, false, false, false, undefined);
-                // With 1 attendee, attendeesMinusOwnerCount = Math.max(0, 1 - 1) = 0, violation should be added
-                expect(result.value).toEqual(expect.arrayContaining([missingAttendeesViolation]));
-            });
-
-            it('should not add missingAttendees violation when 2+ attendees exist (assumes owner is one of them)', () => {
-                transactionViolations = [];
-                transaction.comment = {
-                    attendees: [
-                        {email: 'person1@example.com', displayName: 'Person 1', avatarUrl: ''},
-                        {email: 'person2@example.com', displayName: 'Person 2', avatarUrl: ''},
-                    ],
-                };
-                const result = ViolationsUtils.getViolationsOnyxData(transaction, transactionViolations, policy, policyTags, policyCategories, false, false, false, undefined);
-                // With 2 attendees, attendeesMinusOwnerCount = Math.max(0, 2 - 1) = 1, no violation
-                expect(result.value).not.toEqual(expect.arrayContaining([missingAttendeesViolation]));
-            });
-
-            it('should remove missingAttendees violation when second attendee is added', () => {
-                transactionViolations = [missingAttendeesViolation];
-                transaction.comment = {
-                    attendees: [
-                        {email: 'person1@example.com', displayName: 'Person 1', avatarUrl: ''},
-                        {email: 'person2@example.com', displayName: 'Person 2', avatarUrl: ''},
-                    ],
-                };
-                const result = ViolationsUtils.getViolationsOnyxData(transaction, transactionViolations, policy, policyTags, policyCategories, false, false, false, undefined);
-                // Violation should be removed since we now have 2 attendees
-                expect(result.value).not.toEqual(expect.arrayContaining([missingAttendeesViolation]));
-            });
-        });
-    });
 });
 
 const getFakeTransaction = (transactionID: string, comment?: Transaction['comment']) => ({
