Libra CodeHub

CedarCopilot/cedar-mail

Branch: staging

fix: meeting-prep renders meeting time in user's timezone, not the invite's

merged#2468CedarCopilot

CedarCopilot wants to merge 0 commits into staging from fix/sherlock-meeting-prep-user-timezone-20260813-105621

Live on prod, no production signal yetTimeline and evidence
  1. Opened
    Aug 13, 2026, 11:07 AM
  2. Merged
    Aug 13, 2026, 1:27 PM
  3. Live on prod
    Aug 13, 2026, 1:27 PM
  4. Observed 3 hours
    Aug 13, 2026, 1:27 PM
  5. Pipelines steady after this deploy
    Aug 13, 2026, 1:27 PM
  6. Unobserved

    Live on prod, no production signal yet

    Aug 13, 2026, 4:36 PM

Behaviors Libra is checking

Chat workflow behavior from context-formatting/conversation-context.ts keeps working in prod.Inconclusivelow confidence

Strict CloudWatch fallback found 1 prod failure log lines for mirror.upsertEmailThread failed (write CONNECT_TIMEOUT aws-1-us-east-2.pooler.supabase.com:6543), but this domain-wide failure family is not tied to this intent.

prod, checked Aug 13, 2026, 4:36 PM

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.

What was wrong

Willem (Listen Labs) got a first-meeting prep for Atmosphere TV reading "…at 3:30 PM EDT…". Willem is a Pacific user , the meeting is 12:30 PM PDT.

The "Upcoming scheduled meetings" block in conversation-context.ts (fed to the meeting-prep agent via fetch-conversation) rendered the meeting in the event's own timezone (ev.timezone). A calendar event's timezone comes from Google start.timeZone (services/crm/calendar-events.ts:528) , the zone the invite was authored in. For a rep who mostly receives externally-booked invites, that's the counterpart's zone, not the rep's. The Atmosphere invite came in as America/New_York, so the prep said "3:30 PM EDT".

This is the second-order bug from PR #2462: that fix correctly stopped the LLM from hand-converting UTC (which it did unreliably) by pre-formatting the time , but anchored to the event zone. Every other agent-facing date in Cedar (e.g. the preamble's current_datetime) renders in the user's own zone; this one block diverged.

What changed

formatHydratedConversationForAgent now resolves the executing user's timezone (slackNotifications.digestTimezone ?? emailNotifications.timezone, same source automations.ts / request-context.ts use) and renders the meeting in that frame, appending the invite's own zone in parentheses only when it yields a different wall-clock label:

Atmosphere TV <> Listen pilot scoping sync , Thursday Aug 13, 12:30 PM PDT (3:30 PM EDT)

  • Same-zone meetings stay clean (no parens).
  • Different IANA zones with the same offset at that instant (e.g. NY vs Toronto) → labels equal → no parens.
  • If the user timezone can't be resolved → falls back to the event zone (prior behavior). Best-effort DB lookup wrapped in try/catch; only runs when the conversation has upcoming meetings.

Blast radius

Shared formatter read by fetch-conversation and the on-event orchestrator context → all users. Output only changes for users whose own zone differs from an invite's (e.g. bicoastal reps); for everyone else (invite zone == user zone) it's identical. Same surface PR #2462 touched.

How to test

Re-run a before-meeting prep for a Pacific user with an Eastern-scheduled invite (e.g. Willem / Atmosphere TV, conversation 70385d74-c065-4223-b24a-2b0a0234adb9). The "Upcoming scheduled meetings" line in the fetch-conversation result should read the user's zone first with the invite's zone in parens, e.g. Thursday Aug 13, 12:30 PM PDT (3:30 PM EDT). Verify the downstream notify-user / prep copy uses the user's local time.

Evidence run: 22666629-417b-48c7-9d4b-7065227bc38f.

🤖 Fixed by Sherlock

Greptile Summary

This PR changes upcoming-meeting rendering to prefer the executing user's configured timezone and conditionally show the invite timezone when its wall-clock label differs.

  • Reads the user's Slack or email notification timezone when upcoming meetings exist.
  • Formats the user's local date and time as the primary meeting label.
  • Retains the event timezone as a fallback and optional secondary label.

Confidence Score: 4/5

The PR needs a fallback for invalid stored timezone values before merging because they can abort meeting-prep and conversation-context generation.

The new lookup admits arbitrary stored strings into Intl.DateTimeFormat; malformed values throw outside the database lookup's catch and propagate through critical formatter callers.

Files Needing Attention: apps/server/src/mastra/utils/context-formatting/conversation-context.ts

Important Files Changed

FilenameOverview
apps/server/src/mastra/utils/context-formatting/conversation-context.tsAdds user-timezone meeting rendering, but an invalid stored timezone can now throw and abort agent-context generation instead of falling back.

Flowchart

rendering diagram…
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/mastra/utils/agent-date-formatting.tsno production surface mapped
  • apps/server/src/mastra/utils/context-formatting/conversation-context.tsno production surface mapped