Libra CodeHub

CedarCopilot/cedar-mail

Branch: staging

fix(meetings): route attendee-less recordings via calendar-link attendee match

merged#2477CedarCopilot

CedarCopilot wants to merge 0 commits into staging from fix/meeting-calendar-backstop-attendee-name-match

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

    Live on prod, no production signal yet

    Aug 13, 2026, 11:35 PM

Behaviors Libra is checking

Calendar sync behavior from crm/calendar-events.ts keeps working in prod.Inconclusivelow confidence

Strict CloudWatch fallback found 4 recent prod failure log lines for [SCHEDULED] Failed to enrich unenriched calendar event (No valid connection tokens found for user <id>), but the same failure had 4 log lines in the comparable pre-deploy baseline. Libra is not blaming this PR.

prod, checked Aug 13, 2026, 11:35 PM
CRM integration behavior from crm/meeting-events.ts, crm/utils.ts keeps working in prod.Inconclusivelow confidence

Strict CloudWatch fallback saw 95 success-shaped log lines matching crm, hubspot, salesforce, deal, deals, but no tied operation was present, so Libra is not calling this working.

prod, checked Aug 13, 2026, 11:35 PM

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.

What was wrong

An impromptu Fathom/Teams call arrives with an empty calendar-invitee list, so its stored participants collapse to just the host and its title is generic (Impromptu Microsoft Teams Meeting). The domain-identification agent then has no external attendee to work with and latches onto the only external domain present , the recorder's own fathom.video , routing the meeting onto the pre-existing "Fathom" vendor thread, which sits on a no-op AOP. Everything downstream is skipped: no HubSpot note, no stage evaluation, no recap.

This is exactly how two real Pirros first-meeting demos were lost on Aug 12 , McCulloch England and MMW Architects both landed on the "Fathom" no-op thread instead of their deals.

The calendar-link backstop already existed but couldn't bridge these:

  • Path 1 (Google Calendar event id / iCalUID) , the Fathom payload carried no calendar id.
  • Title fallback (exact title + start-time) , the impromptu title never equals the calendar event's title (Pirros | McCulloch England Architects).

…even though the correct calendar events existed, were linked to the right deals, and sat in the same ±2 min window. The external attendee's email was on the calendar side; the external person's name was in the transcript speaker labels. Neither was being used.

What changed

1. Capture diarized speakers structurally (no regex). At fathom.ts map time, while we already iterate the structured transcript, lift the distinct speaker.display_name (+ any matched_calendar_invitee_email) into a new CedarMeetingData.transcriptSpeakers field , instead of reverse-engineering the flattened "name: text" string downstream.

2. Add an attendee-match tier to the backstop. findConversationFromMeetingCalendarLink now resolves in order of signal strength:

  1. Google Calendar event id / iCalUID , exact unique key (unchanged).
  2. Start-time + external attendee match (new) , by email (exact, when the provider resolved one) or first+last name (the calendar attendee's email resolved to a name via peopleGlobal, matched against the transcript speaker names).
  3. Exact title + start-time , the original fallback, kept, for meetings that carry a real title but no resolvable attendee signal.

Key properties:

  • Never adopts on time alone. Every path except the id match pairs start-time proximity with a second discriminator (an attendee match, or an exact title).
  • Disambiguates back-to-back / recurring calls: each path adopts only when the matching linked events resolve to exactly one conversation.
  • Host is excluded on both sides; first+last is required so a bare first name can't false-match.

Traced against the two lost meetings: both resolve to the correct deal via the attendee tier , Scott Barber → McCulloch, Gabe Christensen → MMW.

Note: the two already-affected recordings were recovered manually (reassigned + re-run); this is the go-forward fix so it can't recur.

How to test

vitest run the two suites:

  • services/crm/__tests__/person-name-match.test.ts , normalization + first/last matching rule.
  • services/crm/__tests__/meeting-calendar-backstop.test.ts , attendee-match adopts; returns false on time-alone; email-match path; displayName path; two-candidate disambiguation; ambiguous → false; host-only → false; plus the restored title-fallback path (adopts on exact title + single distinct; falls through when attendee signal present but unmatched; ambiguous title → false; no valid start-time → false).

18 tests pass. pnpm --filter @zero/server run types clean on touched files; pnpm deps:check clean.

🤖 Generated with Claude Code

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/tools/event-execution/assignEventToConversationTool.tsno production surface mapped
  • apps/server/src/services/crm/__tests__/external-meeting-speakers.test.tsno production surface mapped
  • apps/server/src/services/crm/__tests__/meeting-calendar-backstop.test.tsno production surface mapped
  • apps/server/src/services/crm/__tests__/person-name-match.test.tsno production surface mapped
  • apps/server/src/services/crm/calendar-events.tsno production surface mapped
  • apps/server/src/services/crm/meeting-events.tsno production surface mapped
  • apps/server/src/services/crm/utils.tsno production surface mapped
  • apps/server/src/services/integrations/meetings/fathom.tsno production surface mapped
  • apps/server/src/services/integrations/meetings/types.tsno production surface mapped