Libra CodeHub

CedarCopilot/cedar-mail

Branch: staging

fix(crm): address Greptile review on PR #2882 (already merged)

merged#2892CedarCopilot

CedarCopilot wants to merge 4 commits into staging from fix/2882-greptile-review

What Libra verified

  • Bare date filter values compare at noon UTC

    Libra ran this behavior against the change and confirmed the check detects when it breaks.

    Technical evidence
    • a bare YYYY-MM-DD comparison filter is bound at noon UTC so it can match noon-stamped custom-field content
    • a comparison filter that already includes a time is not rewritten as a bare-date value

    Changed code: conversation-lookup.ts.

Live on prod, watching, 2 days leftTimeline and evidence
  1. Opened
    Sep 22, 2026, 6:38 PM
  2. Sep 22, 2026, 7:46 PM
  3. Merged
    Sep 22, 2026, 7:55 PM
  4. Live on prod
    Sep 22, 2026, 7:55 PM
  5. Observed 0 hours
    Sep 22, 2026, 7:55 PM
  6. Watching

    Live on prod, watching, 2 days left

    Sep 22, 2026, 7:55 PM
  7. Pipelines steady after this deploy
    Sep 22, 2026, 7:55 PM

Behaviors Libra is checking

CRM custom-field gt, gte, lt, and lte filters must continue comparing valid bare dates and ISO datetimes, including millisecond and timezone-offset forms, against the requested timestamp.Not checked
prod
CRM conversation lookups must return no matches instead of failing when custom-field content is arbitrary text, has a garbage suffix, or contains out-of-range date/time or timezone components.Not checked
prod

Libra has verdicts on 0 of 2 tracked behaviors on prod; 2 are still being checked. Libra checks hourly for 3 days after each deploy.

Summary

Greptile reviewed PR #2882 after it was already merged to staging, and surfaced a real P1 correctness bug plus P2 NEVER-cast findings that never got addressed before merge. This PR closes them out.

P1 , the date-guard cast was not actually safe (reproduced live against Postgres)

buildCustomFieldCondition's gt/gte/lt/lte case used regex AND current::timestamptz op value. Two problems, both reproduced live:

  1. Postgres does not guarantee AND operand evaluation order (per the manual: "the inputs of an AND ... are not evaluated left-to-right"), so a bare regex AND cast can still evaluate the cast against non-matching content.
  2. The regex was only a ^ prefix match, not anchored at the end , a custom field's arbitrary text content like "2026-09-21 some unrelated note" matches the prefix and reaches ::timestamptz, which throws on it.

Fix: CASE WHEN <regex> THEN (cast comparison) ELSE false END , CASE WHEN genuinely guarantees its THEN branch only evaluates when the condition is true, unlike AND. The regex is now fully anchored (^...$) and range-constrains month (01-12) and day (01-31).

Documented, accepted residual risk: a value that's calendar-invalid but still date-shaped within those ranges (e.g. "2026-02-30" , Feb has ≤29 days, a rule no regex can express) still reaches the cast and throws , reproduced live. Fully closing that needs a DB-side exception-safe cast function (a migration). Discussed with Isabelle; decided to ship the regex tightening now and not touch the live DB for this, given how narrow/rare that exact shape is for real custom-field text.

P2 , NEVER-cast findings in the test file

  • Two dead casts removed outright: a for...of loop already narrowed to the correct union without help, and this same fix's tightened return type (SQL<unknown>, never null) made a defensive null-guarding cast obsolete.
  • Four "manufacture an invalid value to test the runtime throw" casts replaced with a small untrustedFilter(json: string) helper that parses a JSON string , the same untrusted-string boundary FILTER_OPERATORS' own doc comment describes (the table from clause's JSON-sourced filters), exercised for real instead of overridden with as.

Test plan

  • Live-verified against Postgres, both before and after: "2026-09-21 some unrelated note" and "2026-13-45" crash the old guard, pass cleanly (return false) under the new one.
  • pnpm --filter @zero/server run types , clean
  • pnpm --filter @zero/server exec vitest run on all 5 related test files , 101/101 passing, including 2 new regression tests locking in the tightened regex text
  • No live DB schema/function changes , this PR is code-only

🤖 Generated with Claude Code

RetriggerConfidence Score: 4/5

The behavioral timestamp fix appears sound, but the explicit repository no-cast requirement must be satisfied before merging.

Findings

  1. P1 Malformed times reach cast
  2. P2 Non-null assertion bypasses narrowing
  3. <img alt="P2" src="http
Show production surfaces and changed-file mapping

Production surfaces

Libra has not measured any production surfaces for this change yet.

Changed files → surfaces

  • apps/server/src/services/crm/__tests__/conversation-lookup.filter-conditions.test.tsno production surface mapped
  • apps/server/src/services/crm/__tests__/conversation-lookup.timestamptz-guard.integration.test.tsno production surface mapped
  • apps/server/src/services/crm/conversation-lookup.tsno production surface mapped