CedarCopilot wants to merge 1 commit into staging from fix/find-crm-unknown-column-ids
Live on prod, no production signal yet
Strict CloudWatch fallback found 3 prod failure log lines for mirror.upsertEmailThread failed (write CONNECTION_CLOSED aws-1-us-east-2.pooler.supabase.com:6543), but this domain-wide failure family is not tied to this intent.
Libra has verdicts on 0 of 1 tracked behaviors on prod; 1 is still being checked. Libra checks hourly for 3 days after each deploy.
find-crm-conversations in column_filters mode normalized filters through a switch (columnId). An id with no matching case fell through with no error and no warning: the filter was dropped, the query ran unfiltered, and the tool still returned success: true. The caller has no way to tell "no deals matched" apart from "your filter never applied".
Found in production. A cron subagent filtered { columnId: "hasTodoTasks", binaryChoice: "false" } to skip deals that already have an open follow-up task, got the entire pipeline back, and only avoided acting on it because the model noticed tasks in the results and hand-filtered them out.
hasTodoTasks is a particularly easy id to guess wrong, because it is not wrong anywhere else: it is the name currentTasks normalizes into (findCRMConversationsTool.ts:583 before this change), and it is the spelling the sibling normalizer at services/crm/conversations.ts:6601 accepts.
COLUMN_FILTER_NORMALIZERS replaces the switch: one normalizer per canonical column id. The map is now the single source of truth for what is filterable, so the validator cannot drift from the implementation the way a hand-maintained list would.validateColumnFilterColumns rejects any id that is neither in the map nor wm_-prefixed, and names the valid ids in the error so the caller does not just guess again. Same fail-loud shape as the existing validateColumnFilterKinds guard, which was added for the identical class of bug (silently-dropped searchText on select-only columns).hasTodoTasks ā currentTasks, rather than failing a caller who guessed the internal name.columnId was documented only as 'Column ID to filter', which is what made guessing inevitable.Behavior change worth knowing: a call that previously succeeded-with-a-silently-ignored-filter now returns an error. That is the point, but it means an agent in the habit of passing a junk id will start seeing failures instead of quietly wrong result sets.
pnpm --filter @zero/server exec vitest run src/mastra/tools/conversation/__tests__/findCRMConversationsTool.test.ts , 16 passed, including 3 new cases: unknown id rejected, hasTodoTasks accepted as an alias, wm_ custom fields still pass.pnpm --filter @zero/server run types , clean.pnpm deps:check , no violations (1543 modules, 8064 dependencies).findCRMConversationsTool.ts is 1304 lines, up 32 from 1272. It was already over the 1k budget before this change; decomposing it is worth doing but is out of scope here.
š¤ Generated with Claude Code
This PR makes find-crm-conversations fail loudly when column_filters contains an unsupported column ID while preserving custom fields and documented aliases.
hasTodoTasks as an alias for currentTasks.The PR appears safe to merge, with no concrete changed-code failure identified.
The validator and normalizer use the same canonical map, aliases are normalized before lookup, and custom-field filters continue through their dedicated conversion path.
| Filename | Overview |
|---|---|
| apps/server/src/mastra/tools/conversation/findCRMConversationsTool.ts | Adds canonical column-filter normalization and validation while preserving aliases and the separate custom-field conversion path; no actionable regression was established. |
| apps/server/src/mastra/tools/conversation/tests/findCRMConversationsTool.test.ts | Adds focused regression tests covering unknown |
Libra has not measured any production surfaces for this change yet.