diff --git a/lang/es.json b/lang/es.json
index 9c21139f..0779dbbb 100644
--- a/lang/es.json
+++ b/lang/es.json
@@ -804,8 +804,10 @@
"Manage your profile and account settings": "Gestiona tu perfil y configuración de cuenta",
"Manage your subscription, update payment methods, or view invoices through the Stripe billing portal.": "Gestiona tu suscripción, actualiza métodos de pago o consulta facturas a través del portal de facturación de Stripe.",
"Manage your subscription, update payment methods, or\\n view invoices through the Stripe billing portal.": "Gestiona tu suscripción, actualiza métodos de pago o consulta facturas a través del portal de facturación de Stripe.",
+ "Manage rules that categorize transactions and add labels automatically": "Gestiona reglas que categorizan transacciones y agregan etiquetas automáticamente",
"Manage your transaction automation rules": "Gestiona tus reglas de automatización de transacciones",
"Manage your transaction automation rules. Rules will be applied to categorize transactions automatically.": "Gestiona tus reglas de automatización de transacciones. Las reglas se aplicarán para categorizar transacciones automáticamente.",
+ "Manage your transaction automation rules. Rules will categorize transactions and add labels automatically.": "Gestiona tus reglas de automatización de transacciones. Las reglas categorizarán transacciones y agregarán etiquetas automáticamente.",
"Manage your transaction categories": "Gestiona tus categorías de transacciones",
"Manage your transaction labels": "Gestiona tus etiquetas de transacciones",
"Manage your two-factor authentication settings": "Gestiona tu configuración de autenticación de dos factores",
@@ -1127,6 +1129,7 @@
"Select date column": "Seleccionar columna de fecha",
"Select description column": "Seleccionar columna de descripción",
"Select invested amount column (optional)": "Seleccionar columna de cantidad invertida (opcional)",
+ "Select labels": "Seleccionar etiquetas",
"Select labels (optional)": "Seleccionar etiquetas (opcional)",
"Select labels...": "Seleccionar etiquetas...",
"Select language": "Seleccionar idioma",
diff --git a/resources/js/components/automation-rules/automation-rule-action-badges.tsx b/resources/js/components/automation-rules/automation-rule-action-badges.tsx
new file mode 100644
index 00000000..0beb2997
--- /dev/null
+++ b/resources/js/components/automation-rules/automation-rule-action-badges.tsx
@@ -0,0 +1,51 @@
+import { LabelBadges } from '@/components/shared/label-combobox';
+import { Badge } from '@/components/ui/badge';
+import { type AutomationRule, getRuleActions } from '@/types/automation-rule';
+import { getCategoryColorClasses } from '@/types/category';
+import { __ } from '@/utils/i18n';
+import * as Icons from 'lucide-react';
+
+export function AutomationRuleActionBadges({ rule }: { rule: AutomationRule }) {
+ const actions = getRuleActions(rule);
+ const hasLabels = actions.hasLabels && actions.labels?.length;
+
+ if (!actions.category && !hasLabels && !actions.hasNote) {
+ return (
+
+ {__('No action set')}
+
+ );
+ }
+
+ return (
+
+ {actions.category && }
+
+ {hasLabels && }
+
+ {actions.hasNote && (
+ {__('Add note')}
+ )}
+
+ );
+}
+
+function CategoryBadge({
+ category,
+}: {
+ category: NonNullable['category']>;
+}) {
+ const colorClasses = getCategoryColorClasses(category.color);
+ const IconComponent = Icons[
+ category.icon as keyof typeof Icons
+ ] as Icons.LucideIcon;
+
+ return (
+
+ {IconComponent && }
+ {category.name}
+
+ );
+}
diff --git a/resources/js/components/automation-rules/automation-rules-dialog.tsx b/resources/js/components/automation-rules/automation-rules-dialog.tsx
index 87835ad9..62cd8c0e 100644
--- a/resources/js/components/automation-rules/automation-rules-dialog.tsx
+++ b/resources/js/components/automation-rules/automation-rules-dialog.tsx
@@ -12,14 +12,13 @@ import {
useReactTable,
VisibilityState,
} from '@tanstack/react-table';
-import * as Icons from 'lucide-react';
import { MoreHorizontal } from 'lucide-react';
import { useMemo, useState } from 'react';
+import { AutomationRuleActionBadges } from '@/components/automation-rules/automation-rule-action-badges';
import { CreateAutomationRuleDialog } from '@/components/automation-rules/create-automation-rule-dialog';
import { DeleteAutomationRuleDialog } from '@/components/automation-rules/delete-automation-rule-dialog';
import { EditAutomationRuleDialog } from '@/components/automation-rules/edit-automation-rule-dialog';
-import { Badge } from '@/components/ui/badge';
import { Button } from '@/components/ui/button';
import {
ContextMenu,
@@ -51,8 +50,9 @@ import {
TableHeader,
TableRow,
} from '@/components/ui/table';
-import { type AutomationRule, getRuleActions } from '@/types/automation-rule';
-import { type Category, getCategoryColorClasses } from '@/types/category';
+import { type AutomationRule } from '@/types/automation-rule';
+import { type Category } from '@/types/category';
+import { type Label } from '@/types/label';
import { __ } from '@/utils/i18n';
interface AutomationRulesDialogProps {
@@ -63,9 +63,11 @@ interface AutomationRulesDialogProps {
function AutomationRuleActions({
rule,
categories,
+ labels,
}: {
rule: AutomationRule;
categories: Category[];
+ labels: Label[];
}) {
const [editOpen, setEditOpen] = useState(false);
const [deleteOpen, setDeleteOpen] = useState(false);
@@ -100,6 +102,7 @@ function AutomationRuleActions({
@@ -115,9 +118,11 @@ function AutomationRuleActions({
function AutomationRuleRow({
row,
categories,
+ labels,
}: {
row: Row;
categories: Category[];
+ labels: Label[];
}) {
const rule = row.original;
const [editOpen, setEditOpen] = useState(false);
@@ -163,6 +168,7 @@ function AutomationRuleRow({
@@ -183,8 +189,8 @@ export function AutomationRulesDialog({
automationRules: AutomationRule[];
}>().props;
- // Get categories from globally shared Inertia data
const categories = usePage().props.categories as Category[];
+ const labels = usePage().props.labels as Label[];
const rules = useMemo(
() =>
rawRules.map((rule) => ({
@@ -216,54 +222,7 @@ export function AutomationRulesDialog({
id: 'actions_display',
header: __('Actions'),
cell: ({ row }) => {
- const rule = row.original;
- const actions = getRuleActions(rule);
-
- if (actions.type === 'multiple') {
- return (
-
- {actions.category && (
-
- {(() => {
- const IconComponent = Icons[
- actions.category
- .icon as keyof typeof Icons
- ] as Icons.LucideIcon;
- return IconComponent ? (
-
- ) : null;
- })()}
- {actions.category.name}
-
- )}
-
- );
- }
-
- if (actions.type === 'category' && actions.category) {
- const colorClasses = getCategoryColorClasses(
- actions.category.color,
- );
- const IconComponent = Icons[
- actions.category.icon as keyof typeof Icons
- ] as Icons.LucideIcon;
- return (
-
-
- {actions.category.name}
-
- );
- }
-
- return (
-
- {__('No action set')}
-
- );
+ return ;
},
},
{
@@ -273,6 +232,7 @@ export function AutomationRulesDialog({
),
},
@@ -301,7 +261,7 @@ export function AutomationRulesDialog({
{__('Automation Rules')}
{__(
- 'Manage your transaction automation rules. Rules will be applied to categorize transactions automatically.',
+ 'Manage your transaction automation rules. Rules will categorize transactions and add labels automatically.',
)}
@@ -322,7 +282,10 @@ export function AutomationRulesDialog({
}
className="max-w-sm"
/>
-
+
@@ -356,6 +319,7 @@ export function AutomationRulesDialog({
key={row.id}
row={row}
categories={categories}
+ labels={labels}
/>
))
) : (
diff --git a/resources/js/components/automation-rules/create-automation-rule-dialog.tsx b/resources/js/components/automation-rules/create-automation-rule-dialog.tsx
index b3ed8644..bd66e7a4 100644
--- a/resources/js/components/automation-rules/create-automation-rule-dialog.tsx
+++ b/resources/js/components/automation-rules/create-automation-rule-dialog.tsx
@@ -1,6 +1,7 @@
import { store } from '@/actions/App/Http/Controllers/Settings/AutomationRuleController';
import { RuleBuilder } from '@/components/automation-rules/rule-builder';
import { CategoryCombobox } from '@/components/shared/category-combobox';
+import { LabelCombobox } from '@/components/shared/label-combobox';
import { Button } from '@/components/ui/button';
import { CreateButton } from '@/components/ui/create-button';
import {
@@ -20,18 +21,21 @@ import {
type RuleStructure,
} from '@/lib/rule-builder-utils';
import type { Category } from '@/types/category';
+import type { Label } from '@/types/label';
import { __ } from '@/utils/i18n';
import { router } from '@inertiajs/react';
import { useState } from 'react';
interface CreateAutomationRuleDialogProps {
categories: Category[];
+ labels: Label[];
disabled?: boolean;
onSuccess?: () => void;
}
export function CreateAutomationRuleDialog({
categories,
+ labels,
disabled = false,
onSuccess,
}: CreateAutomationRuleDialogProps) {
@@ -42,6 +46,7 @@ export function CreateAutomationRuleDialog({
groupOperator: 'or',
});
const [categoryId, setCategoryId] = useState
('');
+ const [labelIds, setLabelIds] = useState([]);
const [isSubmitting, setIsSubmitting] = useState(false);
const [errors, setErrors] = useState>({});
@@ -62,10 +67,11 @@ export function CreateAutomationRuleDialog({
return;
}
- if (!categoryId) {
+ if (!categoryId && labelIds.length === 0) {
setErrors((prev) => ({
...prev,
- action_category_id: 'A category is required',
+ action_category_id:
+ 'At least one category or label is required',
}));
return;
}
@@ -81,10 +87,10 @@ export function CreateAutomationRuleDialog({
title: title.trim(),
priority: 0,
rules_json: JSON.stringify(jsonLogic),
- action_category_id: categoryId,
+ action_category_id: categoryId || null,
action_note: null,
action_note_iv: null,
- action_label_ids: null,
+ action_label_ids: labelIds,
},
{
preserveState: true,
@@ -97,6 +103,7 @@ export function CreateAutomationRuleDialog({
groupOperator: 'and',
});
setCategoryId('');
+ setLabelIds([]);
setErrors({});
onSuccess?.();
},
@@ -158,9 +165,21 @@ export function CreateAutomationRuleDialog({
{__('Actions')}
-
- {__('Set Category')}
-
+
+
+ {__('Set Category')}
+
+ {categoryId && (
+ setCategoryId('')}
+ >
+ {__('Clear')}
+
+ )}
+
- {errors.action_category_id && (
+
+ {__('Add Labels')}
+
+
+
+ {(errors.action_category_id ||
+ errors.action_label_ids ||
+ errors['action_label_ids.0']) && (
- {errors.action_category_id}
+ {errors.action_category_id ||
+ errors.action_label_ids ||
+ errors['action_label_ids.0']}
)}
diff --git a/resources/js/components/automation-rules/edit-automation-rule-dialog.tsx b/resources/js/components/automation-rules/edit-automation-rule-dialog.tsx
index b5b638e9..0aa4d651 100644
--- a/resources/js/components/automation-rules/edit-automation-rule-dialog.tsx
+++ b/resources/js/components/automation-rules/edit-automation-rule-dialog.tsx
@@ -1,6 +1,7 @@
import { update } from '@/actions/App/Http/Controllers/Settings/AutomationRuleController';
import { RuleBuilder } from '@/components/automation-rules/rule-builder';
import { CategoryCombobox } from '@/components/shared/category-combobox';
+import { LabelCombobox } from '@/components/shared/label-combobox';
import { Button } from '@/components/ui/button';
import {
Dialog,
@@ -20,6 +21,7 @@ import {
} from '@/lib/rule-builder-utils';
import type { AutomationRule } from '@/types/automation-rule';
import type { Category } from '@/types/category';
+import type { Label as LabelType } from '@/types/label';
import { __ } from '@/utils/i18n';
import { router } from '@inertiajs/react';
import { useEffect, useState } from 'react';
@@ -27,6 +29,7 @@ import { useEffect, useState } from 'react';
interface EditAutomationRuleDialogProps {
rule: AutomationRule;
categories: Category[];
+ labels: LabelType[];
open: boolean;
onOpenChange: (open: boolean) => void;
onSuccess?: () => void;
@@ -35,6 +38,7 @@ interface EditAutomationRuleDialogProps {
export function EditAutomationRuleDialog({
rule,
categories,
+ labels,
open,
onOpenChange,
onSuccess,
@@ -45,6 +49,7 @@ export function EditAutomationRuleDialog({
groupOperator: 'and',
});
const [categoryId, setCategoryId] = useState('');
+ const [labelIds, setLabelIds] = useState([]);
const [isSubmitting, setIsSubmitting] = useState(false);
const [errors, setErrors] = useState>({});
@@ -55,6 +60,7 @@ export function EditAutomationRuleDialog({
setCategoryId(
rule.action_category_id ? String(rule.action_category_id) : '',
);
+ setLabelIds(rule.labels?.map((label) => label.id) ?? []);
}
}, [rule, open]);
@@ -75,10 +81,11 @@ export function EditAutomationRuleDialog({
return;
}
- if (!categoryId) {
+ if (!categoryId && labelIds.length === 0) {
setErrors((prev) => ({
...prev,
- action_category_id: 'A category is required',
+ action_category_id:
+ 'At least one category or label is required',
}));
return;
}
@@ -94,10 +101,10 @@ export function EditAutomationRuleDialog({
title: title.trim(),
priority: rule.priority,
rules_json: JSON.stringify(jsonLogic),
- action_category_id: categoryId,
+ action_category_id: categoryId || null,
action_note: null,
action_note_iv: null,
- action_label_ids: null,
+ action_label_ids: labelIds,
},
{
preserveState: true,
@@ -128,7 +135,7 @@ export function EditAutomationRuleDialog({
{__('Edit Automation Rule')}
{__(
- 'Update the rule to automatically categorize transactions.',
+ 'Update the rule to automatically categorize transactions and add labels.',
)}
@@ -160,9 +167,21 @@ export function EditAutomationRuleDialog({
{__('Actions')}
-
- {__('Set Category')}
-
+
+
+ {__('Set Category')}
+
+ {categoryId && (
+ setCategoryId('')}
+ >
+ {__('Clear')}
+
+ )}
+
- {errors.action_category_id && (
+
+ {__('Add Labels')}
+
+
+
+ {(errors.action_category_id ||
+ errors.action_label_ids ||
+ errors['action_label_ids.0']) && (
- {errors.action_category_id}
+ {errors.action_category_id ||
+ errors.action_label_ids ||
+ errors['action_label_ids.0']}
)}
@@ -194,7 +227,9 @@ export function EditAutomationRuleDialog({
disabled={isSubmitting}
data-testid="submit-automation-rule"
>
- {isSubmitting ? 'Saving...' : 'Save Changes'}
+ {isSubmitting
+ ? __('Saving...')
+ : __('Save Changes')}
diff --git a/resources/js/components/transactions/transaction-list.tsx b/resources/js/components/transactions/transaction-list.tsx
index b913f76f..fdff7a91 100644
--- a/resources/js/components/transactions/transaction-list.tsx
+++ b/resources/js/components/transactions/transaction-list.tsx
@@ -59,6 +59,7 @@ import { decrypt, importKey } from '@/lib/crypto';
import { consoleDebug } from '@/lib/debug';
import { db } from '@/lib/dexie-db';
import { getStoredKey } from '@/lib/key-storage';
+import { mergeReEvaluatedTransaction } from '@/lib/transaction-re-evaluation';
import { transactionSyncService } from '@/services/transaction-sync';
import { type Account, type Bank } from '@/types/account';
import { type AutomationRule } from '@/types/automation-rule';
@@ -861,14 +862,9 @@ export function TransactionList({
const updated = response.data.data;
- updateTransaction({
- ...transaction,
- category_id: updated.category_id,
- category: updated.category,
- labels: updated.labels,
- notes: updated.notes,
- notes_iv: updated.notes_iv,
- });
+ updateTransaction(
+ mergeReEvaluatedTransaction(transaction, updated),
+ );
consoleDebug('✓ UI state updated successfully');
} catch (error) {
consoleDebug('❌ Error during re-evaluation:', error);
diff --git a/resources/js/lib/transaction-re-evaluation.test.ts b/resources/js/lib/transaction-re-evaluation.test.ts
new file mode 100644
index 00000000..9aa851a9
--- /dev/null
+++ b/resources/js/lib/transaction-re-evaluation.test.ts
@@ -0,0 +1,64 @@
+import { describe, expect, it } from 'vitest';
+
+import { type DecryptedTransaction } from '@/types/transaction';
+import { mergeReEvaluatedTransaction } from './transaction-re-evaluation';
+
+function transaction(
+ overrides: Partial = {},
+): DecryptedTransaction {
+ return {
+ id: 'transaction-1',
+ user_id: 'user-1',
+ account_id: 'account-1',
+ category_id: null,
+ description: 'Coffee',
+ description_iv: null,
+ transaction_date: '2026-05-11',
+ amount: -450,
+ currency_code: 'EUR',
+ notes: null,
+ notes_iv: null,
+ source: 'imported',
+ label_ids: [],
+ created_at: '2026-05-11T00:00:00.000000Z',
+ updated_at: '2026-05-11T00:00:00.000000Z',
+ decryptedDescription: 'Coffee',
+ decryptedNotes: null,
+ ...overrides,
+ };
+}
+
+describe('mergeReEvaluatedTransaction', () => {
+ it('derives label ids from returned labels', () => {
+ const merged = mergeReEvaluatedTransaction(
+ transaction(),
+ transaction({
+ labels: [
+ {
+ id: 'label-1',
+ user_id: 'user-1',
+ name: 'Work',
+ color: 'blue',
+ created_at: '2026-05-11T00:00:00.000000Z',
+ updated_at: '2026-05-11T00:00:00.000000Z',
+ deleted_at: null,
+ },
+ ],
+ }),
+ );
+
+ expect(merged.label_ids).toEqual(['label-1']);
+ });
+
+ it('updates plaintext notes for immediate display', () => {
+ const merged = mergeReEvaluatedTransaction(
+ transaction(),
+ transaction({
+ notes: 'Needs review',
+ notes_iv: null,
+ }),
+ );
+
+ expect(merged.decryptedNotes).toBe('Needs review');
+ });
+});
diff --git a/resources/js/lib/transaction-re-evaluation.ts b/resources/js/lib/transaction-re-evaluation.ts
new file mode 100644
index 00000000..627f50ad
--- /dev/null
+++ b/resources/js/lib/transaction-re-evaluation.ts
@@ -0,0 +1,25 @@
+import { type DecryptedTransaction } from '@/types/transaction';
+
+export function mergeReEvaluatedTransaction(
+ transaction: DecryptedTransaction,
+ updated: DecryptedTransaction,
+): DecryptedTransaction {
+ const labels = updated.labels ?? transaction.labels;
+ const labelIds = updated.labels
+ ? updated.labels.map((label) => label.id)
+ : (updated.label_ids ?? transaction.label_ids);
+
+ return {
+ ...transaction,
+ category_id: updated.category_id,
+ category: updated.category,
+ labels,
+ label_ids: labelIds,
+ notes: updated.notes,
+ notes_iv: updated.notes_iv,
+ decryptedNotes:
+ updated.notes_iv === null
+ ? (updated.notes ?? null)
+ : transaction.decryptedNotes,
+ };
+}
diff --git a/resources/js/pages/settings/automation-rules.tsx b/resources/js/pages/settings/automation-rules.tsx
index b12be01c..559c4d15 100644
--- a/resources/js/pages/settings/automation-rules.tsx
+++ b/resources/js/pages/settings/automation-rules.tsx
@@ -12,16 +12,15 @@ import {
useReactTable,
VisibilityState,
} from '@tanstack/react-table';
-import * as Icons from 'lucide-react';
import { MoreHorizontal } from 'lucide-react';
import { useMemo, useState } from 'react';
import { index as automationRulesIndex } from '@/actions/App/Http/Controllers/Settings/AutomationRuleController';
+import { AutomationRuleActionBadges } from '@/components/automation-rules/automation-rule-action-badges';
import { CreateAutomationRuleDialog } from '@/components/automation-rules/create-automation-rule-dialog';
import { DeleteAutomationRuleDialog } from '@/components/automation-rules/delete-automation-rule-dialog';
import { EditAutomationRuleDialog } from '@/components/automation-rules/edit-automation-rule-dialog';
import HeadingSmall from '@/components/heading-small';
-import { Badge } from '@/components/ui/badge';
import { Button } from '@/components/ui/button';
import {
ContextMenu,
@@ -49,8 +48,9 @@ import {
import AppLayout from '@/layouts/app-layout';
import SettingsLayout from '@/layouts/settings/layout';
import { type BreadcrumbItem } from '@/types';
-import { type AutomationRule, getRuleActions } from '@/types/automation-rule';
-import { type Category, getCategoryColorClasses } from '@/types/category';
+import { type AutomationRule } from '@/types/automation-rule';
+import { type Category } from '@/types/category';
+import { type Label } from '@/types/label';
import { __ } from '@/utils/i18n';
const breadcrumbs: BreadcrumbItem[] = [
@@ -63,9 +63,11 @@ const breadcrumbs: BreadcrumbItem[] = [
function AutomationRuleActions({
rule,
categories,
+ labels,
}: {
rule: AutomationRule;
categories: Category[];
+ labels: Label[];
}) {
const [editOpen, setEditOpen] = useState(false);
const [deleteOpen, setDeleteOpen] = useState(false);
@@ -100,6 +102,7 @@ function AutomationRuleActions({
@@ -115,9 +118,11 @@ function AutomationRuleActions({
function AutomationRuleRow({
row,
categories,
+ labels,
}: {
row: Row;
categories: Category[];
+ labels: Label[];
}) {
const rule = row.original;
const [editOpen, setEditOpen] = useState(false);
@@ -163,6 +168,7 @@ function AutomationRuleRow({
@@ -180,8 +186,8 @@ export default function AutomationRules() {
automationRules: AutomationRule[];
}>().props;
- // Get categories from globally shared Inertia data
const categories = usePage().props.categories as Category[];
+ const labels = usePage().props.labels as Label[];
const rules = useMemo(
() =>
rawRules.map((rule) => ({
@@ -213,54 +219,7 @@ export default function AutomationRules() {
id: 'actions_display',
header: __('Actions'),
cell: ({ row }) => {
- const rule = row.original;
- const actions = getRuleActions(rule);
-
- if (actions.type === 'multiple') {
- return (
-
- {actions.category && (
-
- {(() => {
- const IconComponent = Icons[
- actions.category
- .icon as keyof typeof Icons
- ] as Icons.LucideIcon;
- return IconComponent ? (
-
- ) : null;
- })()}
- {actions.category.name}
-
- )}
-
- );
- }
-
- if (actions.type === 'category' && actions.category) {
- const colorClasses = getCategoryColorClasses(
- actions.category.color,
- );
- const IconComponent = Icons[
- actions.category.icon as keyof typeof Icons
- ] as Icons.LucideIcon;
- return (
-
-
- {actions.category.name}
-
- );
- }
-
- return (
-
- {__('No action set')}
-
- );
+ return ;
},
},
{
@@ -270,6 +229,7 @@ export default function AutomationRules() {
),
},
@@ -300,7 +260,7 @@ export default function AutomationRules() {
@@ -322,6 +282,7 @@ export default function AutomationRules() {
/>
@@ -363,6 +324,7 @@ export default function AutomationRules() {
key={row.id}
row={row}
categories={categories}
+ labels={labels}
/>
))
) : (
diff --git a/resources/js/pages/transactions/index.tsx b/resources/js/pages/transactions/index.tsx
index 11a13ec7..e6f7211f 100644
--- a/resources/js/pages/transactions/index.tsx
+++ b/resources/js/pages/transactions/index.tsx
@@ -67,6 +67,7 @@ import {
isCursorPaginatedResponse,
} from '@/lib/cursor-pagination';
import { consoleDebug } from '@/lib/debug';
+import { mergeReEvaluatedTransaction } from '@/lib/transaction-re-evaluation';
import { cn } from '@/lib/utils';
import { transactionSyncService } from '@/services/transaction-sync';
import { type BreadcrumbItem } from '@/types';
@@ -662,14 +663,9 @@ export default function Transactions({
const updated = response.data.data;
- updateTransaction({
- ...transaction,
- category_id: updated.category_id,
- category: updated.category,
- labels: updated.labels,
- notes: updated.notes,
- notes_iv: updated.notes_iv,
- });
+ updateTransaction(
+ mergeReEvaluatedTransaction(transaction, updated),
+ );
} catch (error) {
console.error('Failed to re-evaluate rules:', error);
} finally {