diff --git a/lang/es.json b/lang/es.json
index 405d4e69..7e3f2c96 100644
--- a/lang/es.json
+++ b/lang/es.json
@@ -2,6 +2,7 @@
"Categorized by AI": "Categorizado por IA",
"Categorized by AI with :confidence% confident": "Categorizado por IA con un :confidence % de confianza",
"Only show AI guesses": "Mostrar solo sugerencias de IA",
+ "Created by AI": "Creado por IA",
"Analyze": "Analizar",
"Monthly average": "Media mensual",
"Trend": "Tendencia",
diff --git a/resources/js/components/automation-rules/automation-rule-title.test.tsx b/resources/js/components/automation-rules/automation-rule-title.test.tsx
new file mode 100644
index 00000000..ecd2fdf5
--- /dev/null
+++ b/resources/js/components/automation-rules/automation-rule-title.test.tsx
@@ -0,0 +1,41 @@
+import { render, screen } from '@testing-library/react';
+import { describe, expect, it } from 'vitest';
+
+import { type AutomationRule, type RuleOrigin } from '@/types/automation-rule';
+import { AutomationRuleTitle } from './automation-rule-title';
+
+function makeRule(origin: RuleOrigin): AutomationRule {
+ return {
+ id: 'rule-1',
+ user_id: 'user-1',
+ title: 'Supermarket',
+ priority: 1,
+ origin,
+ rules_json: {},
+ action_category_id: null,
+ action_note: null,
+ action_note_iv: null,
+ labels: [],
+ created_at: '2026-06-15T00:00:00Z',
+ updated_at: '2026-06-15T00:00:00Z',
+ deleted_at: null,
+ };
+}
+
+describe('AutomationRuleTitle', () => {
+ it('prefixes the title with the AI sparkle when the rule was created by AI', () => {
+ render();
+
+ expect(screen.getByText('Supermarket')).toBeInTheDocument();
+ expect(screen.getByLabelText('Created by AI')).toBeInTheDocument();
+ });
+
+ it('shows no marker for user-created rules', () => {
+ render();
+
+ expect(screen.getByText('Supermarket')).toBeInTheDocument();
+ expect(
+ screen.queryByLabelText('Created by AI'),
+ ).not.toBeInTheDocument();
+ });
+});
diff --git a/resources/js/components/automation-rules/automation-rule-title.tsx b/resources/js/components/automation-rules/automation-rule-title.tsx
new file mode 100644
index 00000000..9cf72261
--- /dev/null
+++ b/resources/js/components/automation-rules/automation-rule-title.tsx
@@ -0,0 +1,39 @@
+import { AiSparkleIcon } from '@/components/ui/ai-sparkle-icon';
+import {
+ Tooltip,
+ TooltipContent,
+ TooltipProvider,
+ TooltipTrigger,
+} from '@/components/ui/tooltip';
+import { type AutomationRule } from '@/types/automation-rule';
+import { __ } from '@/utils/i18n';
+
+/**
+ * A rule's title, prefixed with the shared AI sparkle when the rule itself was
+ * generated by AI (origin === 'ai').
+ */
+export function AutomationRuleTitle({ rule }: { rule: AutomationRule }) {
+ const isAiGenerated = rule.origin === 'ai';
+ const label = __('Created by AI');
+
+ return (
+
+ {isAiGenerated && (
+
+
+
+
+
+
+
+ {label}
+
+
+ )}
+ {rule.title}
+
+ );
+}
diff --git a/resources/js/components/transactions/category-cell.tsx b/resources/js/components/transactions/category-cell.tsx
index bb887a28..a285c6cf 100644
--- a/resources/js/components/transactions/category-cell.tsx
+++ b/resources/js/components/transactions/category-cell.tsx
@@ -1,5 +1,5 @@
-import { AiSparkleIcon } from '@/components/transactions/ai-sparkle-icon';
import { CategorySelect } from '@/components/transactions/category-select';
+import { AiSparkleIcon } from '@/components/ui/ai-sparkle-icon';
import {
Tooltip,
TooltipContent,
diff --git a/resources/js/components/transactions/transaction-filters.tsx b/resources/js/components/transactions/transaction-filters.tsx
index 060fae14..093631d5 100644
--- a/resources/js/components/transactions/transaction-filters.tsx
+++ b/resources/js/components/transactions/transaction-filters.tsx
@@ -6,8 +6,8 @@ import { ChevronsUpDown, Tag, X } from 'lucide-react';
import { type ReactNode, useEffect, useMemo, useState } from 'react';
import { AccountName } from '@/components/accounts/account-name';
-import { AiSparkleIcon } from '@/components/transactions/ai-sparkle-icon';
import { SavedFilters } from '@/components/transactions/saved-filters';
+import { AiSparkleIcon } from '@/components/ui/ai-sparkle-icon';
import { Badge } from '@/components/ui/badge';
import { Button } from '@/components/ui/button';
import { Checkbox } from '@/components/ui/checkbox';
diff --git a/resources/js/components/transactions/ai-sparkle-icon.tsx b/resources/js/components/ui/ai-sparkle-icon.tsx
similarity index 84%
rename from resources/js/components/transactions/ai-sparkle-icon.tsx
rename to resources/js/components/ui/ai-sparkle-icon.tsx
index 733a66d3..cb7047c2 100644
--- a/resources/js/components/transactions/ai-sparkle-icon.tsx
+++ b/resources/js/components/ui/ai-sparkle-icon.tsx
@@ -2,7 +2,10 @@ import { cn } from '@/lib/utils';
import { Sparkles } from 'lucide-react';
/**
- * The Gemini-style multi-color sparkle used to mark an AI-guessed category.
+ * The Gemini-style multi-color sparkle that marks anything powered by AI
+ * (AI-guessed categories, AI-generated rules, AI filters, ...). This is the
+ * single shared icon to reuse wherever a feature involves AI.
+ *
* The gradient is applied to the SVG via a referenced linearGradient so the
* stroke and fill render as a true multi-color gradient (not a flat color).
*/
diff --git a/resources/js/pages/settings/automation-rules.tsx b/resources/js/pages/settings/automation-rules.tsx
index dd436b4c..7bc37f68 100644
--- a/resources/js/pages/settings/automation-rules.tsx
+++ b/resources/js/pages/settings/automation-rules.tsx
@@ -18,6 +18,7 @@ import { useMemo, useState } from 'react';
import { index as automationRulesIndex } from '@/actions/App/Http/Controllers/Settings/AutomationRuleController';
import { ApplyAutomationRuleDialog } from '@/components/automation-rules/apply-automation-rule-dialog';
import { AutomationRuleActionBadges } from '@/components/automation-rules/automation-rule-action-badges';
+import { AutomationRuleTitle } from '@/components/automation-rules/automation-rule-title';
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';
@@ -230,9 +231,7 @@ export default function AutomationRules() {
accessorKey: 'title',
header: __('Title'),
cell: ({ row }) => {
- return (
- {row.getValue('title')}
- );
+ return ;
},
},
{
diff --git a/resources/js/types/automation-rule.ts b/resources/js/types/automation-rule.ts
index 52e79ad6..fe80f41f 100644
--- a/resources/js/types/automation-rule.ts
+++ b/resources/js/types/automation-rule.ts
@@ -2,11 +2,14 @@ import type { Category } from './category';
import type { Label } from './label';
import { UUID } from './uuid';
+export type RuleOrigin = 'user' | 'ai';
+
export interface AutomationRule {
id: UUID;
user_id: UUID;
title: string;
priority: number;
+ origin: RuleOrigin;
rules_json: Record;
action_category_id: UUID | null;
action_note: string | null;