diff --git a/apps/backend/src/lib/ai/prompts.ts b/apps/backend/src/lib/ai/prompts.ts
index eca03b24f..5e7ce2809 100644
--- a/apps/backend/src/lib/ai/prompts.ts
+++ b/apps/backend/src/lib/ai/prompts.ts
@@ -44,15 +44,11 @@ export const SYSTEM_PROMPT_IDS = [
"create-dashboard",
"run-query",
"build-analytics-query",
- "filter-analytics-table",
"rewrite-template-source",
] as const;
-export type SystemPromptId = typeof SYSTEM_PROMPT_IDS[number];
-
/**
* Schema documentation for the analytics ClickHouse tables, shared by every
- * prompt that generates SQL against them ("build-analytics-query" and
- * "filter-analytics-table") so the two can never drift apart.
+ * prompt that generates SQL against them ("build-analytics-query") so the two can never drift apart.
*/
const ANALYTICS_TABLES_SCHEMA_DOC = `### DATA SCHEMA (project/branch filtering is automatic — do NOT add WHERE project_id = ...)
@@ -240,6 +236,8 @@ Key insights: No timestamp column — do NOT attempt \`ORDER BY created_at\` or
| provider_account_id | String | External account identifier |
| created_at | DateTime64(3, 'UTC') | Link time |`;
+export type SystemPromptId = typeof SYSTEM_PROMPT_IDS[number];
+
/**
* Context-specific system prompts that are appended to the base prompt.
* These should be concise and focus on the specific use case.
@@ -1315,46 +1313,6 @@ GROUP BY date, event_type ORDER BY date DESC, event_count DESC LIMIT 100
- If the user refers to a previous query, modify it incrementally — don't start from scratch.
- If \`queryAnalytics\` returns an error, adjust and retry. Do NOT invent columns or fabricate data.
- If the user asks about event types or data that don't exist in the schema above, explain what IS available and generate the closest useful query instead.
-`,
-
- "filter-analytics-table": `
-## Context: Analytics Table Row Filter
-
-You power the search bar above a data grid on the Hexclave analytics page. The grid shows every column of ONE table (\`SELECT * FROM
\`), and the user typed a request that a plain substring search can't express. Your ONLY job is to narrow down WHICH ROWS are shown — the set of columns must never change. The table the user is viewing is stated at the start of the conversation.
-
-**HARD RULES — query shape:**
-Call \`queryAnalytics\` with a query of EXACTLY this shape:
-
-\`SELECT * FROM WHERE \`
-
-1. The query MUST start with \`SELECT * FROM \` for the exact table the user is viewing. The frontend validates this shape and rejects anything else.
-2. After the table name, only a \`WHERE\` clause is allowed at the top level — no column lists, aliases, aggregates, GROUP BY, ORDER BY, LIMIT, JOIN, or UNION. The grid layers sorting and pagination on top itself.
-3. Subqueries INSIDE the WHERE condition are allowed and encouraged for cross-table filters, e.g. \`WHERE toString(id) IN (SELECT user_id FROM events WHERE ...)\`. Mind the types: \`users.id\` is a UUID while \`events.user_id\` is a String, so wrap the UUID side in \`toString()\`.
-4. Do NOT paste SQL into chat text in place of a tool call — the UI only picks up tool calls, and only after you come to a complete stop, so avoid chatty responses.
-5. You only see a small preview of the results in the tool output — the user sees the full filtered table in the grid.
-6. If \`queryAnalytics\` returns an error, fix the condition and retry.
-7. If the user refines the filter (e.g. "only verified ones"), modify your previous WHERE condition incrementally.
-8. If the request CANNOT be expressed as a row filter on this table (e.g. it asks for aggregations, trends, grouped counts, joins that add columns, or data from a different table), do NOT call the tool. Reply with ONE short sentence explaining why, and if possible suggest the closest row filter you COULD apply instead.
-
-${ANALYTICS_TABLES_SCHEMA_DOC}
-
-### CRITICAL SQL RULES
-
-1. **JSON extraction REQUIRES toString() wrapper:**
- - CORRECT: \`JSONExtractString(toString(data), 'path')\`
- - WRONG: \`JSONExtractString(data, 'path')\` — this WILL FAIL
-2. **Nested JSON uses dot notation:** \`JSONExtractString(toString(data), 'ip_info.country_code')\`
-3. SELECT queries only — no INSERT / UPDATE / DELETE / DDL
-4. Use relative date ranges (\`now() - INTERVAL X DAY\`) and ClickHouse date helpers: toDate(), toStartOfDay(), toStartOfWeek(), toStartOfMonth()
-5. team_id is always NULL — never filter on it
-6. Prefer case-insensitive matching (\`ILIKE '%...%'\`) for user-typed text unless the user clearly means an exact value
-
-### EXAMPLES (user is viewing \`users\`)
-
-- "signed up in the last 7 days" → \`SELECT * FROM users WHERE signed_up_at >= now() - INTERVAL 7 DAY\`
-- "verified gmail accounts" → \`SELECT * FROM users WHERE primary_email_verified = 1 AND primary_email ILIKE '%@gmail.com%'\`
-- "people with a page view this week" → \`SELECT * FROM users WHERE toString(id) IN (SELECT user_id FROM events WHERE event_type = '$page-view' AND event_at >= now() - INTERVAL 7 DAY)\`
-- "signups per day last month" → no tool call; explain that's an aggregation this view can't display, and offer e.g. "show signups from last month" as a filter instead.
`,
"rewrite-template-source": `You rewrite email template TSX source into standalone draft TSX.