CedarCopilot wants to merge 3 commits into staging from worktree-agent-a0f9ba015e36f0475
Libra ran this behavior against the change and confirmed the check detects when it breaks.
Changed code: findCRMConversationsTool.ts.
Libra ran this behavior against the change and confirmed the check detects when it breaks.
Changed code: conversation-lookup.ts.
Libra ran this behavior against the change and confirmed the check detects when it breaks.
Changed code: conversation-lookup.ts.
Live on prod, watching, 2 days left
Libra has verdicts on 0 of 4 tracked behaviors on prod; 4 are still being checked. Libra checks hourly for 3 days after each deploy.
A chat-agent query filtering {field: "wm_close_date", operator: "gte", value: "2026-09-21"} (+ a paired lte) to answer "show me deals closing this week" silently ran unfiltered against the entire Deals AOP instead of erroring , the tool reported success: true with a wrong result set, and the agent had no signal that its date filter never applied. It then improvised a confused, hedged answer over data it assumed was already filtered.
Root cause: close_date is a real, correctly-mapped custom field , the field reference was right. But buildCustomFieldCondition in conversation-lookup.ts only implemented eq/neq/like/ilike/in/notIn/isNull/isNotNull for custom fields; its default branch for gt/gte/lt/lte did console.warn(...) (server logs only, invisible to the caller) and return null. lookupConversations's if (condition) conditions.push(condition) then silently dropped that null from the WHERE clause. The only filter left standing was aopName = 'Deals', so the query ran against the whole AOP.
The same silent-null-drop pattern also existed for: an unrecognized field/operator on a standard field, a non-array value on in/notIn, and , more subtly , an empty array on in/notIn (which used to silently drop the filter entirely, turning "match nothing" into "no filter applied").
buildFilterCondition / buildCustomFieldCondition now return a real SQL condition for every case, or throw , never a silent null. lookupConversations already had a top-level try/catch that turns a thrown error into { success: false, errorMessage }, so no new error-handling plumbing was needed; a bad filter now fails loudly through the existing path instead of vanishing.
Also adds real gt/gte/lt/lte support for date-shaped custom field values , the actual capability gap behind the incident, since "deals closing this week" against a date custom field is a completely reasonable query. Guarded with a regex check in the SQL itself (content ~ '^\d{4}-\d{2}-\d{2}') before casting to timestamptz, so a non-date field's content just matches nothing rather than throwing a Postgres cast error.
One bug caught by the new test itself, worth calling out: the date-guard regex initially used un-doubled \d inside a JS template literal , \d is not a recognized JS escape sequence, so JS silently strips it to d, which would have sent Postgres the regex ^d{4}-d{2}-d{2} (four literal d characters, never matching a real date) instead of a real guard. The test's SQL-text assertion caught this immediately; fixed to \\d.
Scoped intentionally: buildEventCountCondition/buildEventTypeDateCondition (the eventFilter path) have the identical silent-null-drop pattern for an invalid event type or unparseable date, surfaced during review. Left untouched here on purpose , same bug class, but a different, unrelated code path, and this PR stays focused on the incident it was scoped to fix. Filed as a separate follow-up (Linear ticket linked below once created).
conversation-lookup.filter-conditions.test.ts, 15 cases) reproduces the exact filter shape from the live incident and asserts a real gte/timestamptz condition is built, not a dropped onein/notIn on both custom and standard fields, non-array value on in/notIn throwing, computed binary fields (crmSynced/hasFutureCalendar) still building correctlypnpm --filter @zero/server run types , 0 errorspnpm --filter @zero/server exec vitest run on the new test file + findCRMConversationsTool.test.ts + findCRMConversationsTool.default-aop-scope.test.ts + table-source.test.ts , 63 tests, all passlookupConversations (findCRMConversationsTool.ts, resolveAndAttachTool.ts, `taLibra has not measured any production surfaces for this change yet.