Libra CodeHub

CedarCopilot/cedar-mail

Branch: staging

fix(crm): ambiguous meeting company-assignment , record, don't discard

merged#2779CedarCopilot

CedarCopilot wants to merge 4 commits into staging from fix/meeting-conversation-ambiguity

Live on prod since Mon, 3 of 11 surfaces workingTimeline and evidence
  1. Opened
    Sep 14, 2026, 5:48 AM
  2. Sep 14, 2026, 6:34 AM
  3. Merged
    Sep 14, 2026, 11:26 AM
  4. Live on prod
    Sep 14, 2026, 11:26 AM
  5. Observed 2 days, 11 surfaces, 203 requests
    Sep 14, 2026, 11:26 AM
  6. Pipelines steady after this deploy
    Sep 14, 2026, 11:26 AM
  7. Verified

    Live on prod since Mon, 3 of 11 surfaces working

    Sep 17, 2026, 10:32 AM

Behaviors Libra is checking

Webhook redeliveries of the same unattributed meeting do not send duplicate ambiguity notifications.Inconclusivelow confidence

Verification reached its 130s outer budget before the verifier returned. Libra is keeping this intent verifying until the next check.

prod, checked Sep 17, 2026, 6:30 AM
Each newly persisted unattributed meeting sends a meeting-specific Slack ambiguity notification to the user.Inconclusivelow confidence

Verification reached its 120s wall-clock budget before a tied verdict. Libra is keeping this intent verifying until the next check.

prod, checked Sep 17, 2026, 6:32 AM
Using Find it lets the investigation agent search for the correct company or conversation and move the persisted meeting event into the resolved conversation.Inconclusivelow confidence

Strict CloudWatch fallback saw 7 success-shaped log lines matching conversationsearchcommandbar, overviewdoctab, markmeetingasfailed, shouldupdateeventconversationid, eval-domain-identification, but no tied operation was present, so Libra is not calling this working.

prod, checked Sep 17, 2026, 7:30 AM
Meeting event objects now carry a nullable backend-only ambiguityPingedAt field initialized to null so ambiguity-ping bookkeeping does not surface in the UI.Inconclusivemedium confidence

Post-deploy production telemetry confirms the meeting surface is active: 3 successful cron.processMeetingBackgroundSync spans, 21 handle_execute_meeting spans across 13 users, 6 Granola webhook spans, and repeated createAndLinkEvents meeting-event logs with isNew:true. No.

prod, checked Sep 17, 2026, 7:32 AM
For meetings without a real external participant, the domain-identification agent no longer treats the recording or meeting platform domain as the counterparty or as an automated sender.Inconclusivelow confidence

Verification reached its 130s outer budget before the verifier returned. Libra is keeping this intent verifying until the next check.

prod, checked Sep 17, 2026, 8:30 AM
The domain-identification agent no longer assigns a meeting to a vendor, competitor, or software product merely because it is mentioned as something the prospect uses, evaluates, or compares against.Inconclusivemedium confidence

Post-deploy CloudWatch telemetry shows 11,742 selectPrimaryCompany executions and 11,742 domain-identification result logs, with repeated concrete meeting outcomes returning conversationId=null and primaryDomain=null for meetings lacking grounded external participants.

prod, checked Sep 17, 2026, 8:32 AM

Libra measured 3 of 11 production surfaces on prod. 2 surfaces had under 20 requests, so Libra has not judged them; 6 surfaces had no requests at all. Libra checks hourly for 3 days after each deploy.

Summary

Three confirmed Pirros/Vooma incidents where meeting recordings got misrouted or silently discarded when Cedar couldn't confidently attribute them to a conversation:

  • Calendar backstop window too tight , a real, already-linked calendar event started 3m15s after the recorder's reported meeting time; the prior 2-minute window excluded it. Widened to 7 minutes.
  • Domain-identification misattributing to tools/vendor platforms , a TMS tool mentioned 28x in a transcript got fuzzy-matched as the counterparty (Vooma/McLeod); a meeting-notetaker's own recording-platform domain got used as a fallback answer when no sender existed to check (Pirros/Reza). Both fixed with eval-validated prompt instructions.
  • Unattributed meetings vanished with no trace , pre-execution-setup.ts threw before createAndLinkEvents() ever ran, so a meeting Cedar couldn't attribute left no crm_events row at all , nothing to notify about, nothing to fix later. Extended the nullable-conversation_id model email already uses (docs/unknown-removal.md) to meetings: the event is now recorded unattributed, and a single-button Slack ping lets the rep resolve it (reusing the existing move_event mutation and the existing agent_prompt/operation-registry machinery , no new plumbing).

Two real Pirros meetings (Reza/K2M Design, JEO Consulting) that were sitting on the generic "Fathom" vendor bucket were moved to their correct conversations as a worked example of the resolution path.

Full design doc: apps/server/docs/design/ambiguous-meeting-company-assignment.md.

What changed

  1. calendar-events.ts , 2min → 7min matching window + decision-point logging.
  2. domain-identification.ts , two instructions (competitor/tool-role rule, meeting-has-no-sender rule), validated via the playbook-instruction-eval harness against real production transcripts (both went from ~4-12% failure to 0/8-24 trials).
  3. meeting-events.ts , findOrCreateMeetingEvent accepts conversationId: string | null; never downgrades an already-linked event to unattributed on a failed re-resolution (shouldUpdateEventConversationId).
  4. pre-execution-setup.ts , records the meeting unattributed and pings before the (unchanged) ExpectedTerminationError throw, meetings only.
  5. New meeting-ambiguity-ping{,-message}.ts , decision (dedupe on new-event-only, no kill switch) + message/send, mirroring the existing services/aop/ambiguity-ping{,-message}.ts pattern.

Review

Ran this repo's thermo-review skill against the full diff. One real bug found and fixed during review: the ping rendered meeting times in the server's local timezone instead of the recipient's own (every other time-formatting call site in this codebase resolves slackNotifications.digestTimezone) , fixed, with a deterministic test proving the same UTC instant renders correctly in two different timezones. Also added the dependency-direction justification comment the AOP-ping file carries for its equivalent type-only import. No blocking issues.

Two open questions from the review, left to reviewer judgment rather than answered unilaterally:

  • No feature flag ships with this (explicit decision , the ping only fires on a rare failure branch, not a hot path). No per-org escape hatch exists if it turns out noisier than expected for some org's meeting mix.
  • Whether any existing "all meetings" list surface reads crm_events without filtering on conversation_id IS NOT NULL wasn't traced , if one exists, an unattributed meeting could surface somewhere unexpected before a human resolves it.

Test plan

  • pnpm --filter @zero/server run types , clean
  • pnpm --filter @zero/server run types:test , clean except pre-existing unrelated failures (confirmed via git show HEAD:... predate this branch)
  • pnpm deps:check , 0 violations
  • New/touched test files: 36/36 passing (meeting-ambiguity-ping.test.ts, meeting-ambiguity-ping-message.test.ts, `meeting-events.shouldUp
Show production surfaces and changed-file mapping

Production surfaces

SurfaceRequestsErrorsp95UsersVerdict
orchestrator_agent_step43 → 960 → 0 (0%)128144 ms → 247979 ms37Working
96 requests since the deploy with 0 errors (0 errors in the 43-request baseline before it). p95 247979 ms, was 128144 ms.
model_chunk On-Event Execution Agent (Understudy)31 → 590 → 0 (0%)1 ms → 1 ms0Working
59 requests since the deploy with 0 errors (0 errors in the 31-request baseline before it). p95 1 ms, was 1 ms.
model_chunk Automation Agent (Understudy)11 → 230 → 0 (0%)1 ms → 1 ms0Working
23 requests since the deploy with 0 errors (0 errors in the 11-request baseline before it). p95 1 ms, was 1 ms.
model_step On-Event Execution Agent (Understudy)10 → 190 → 0 (0%)23743 ms → 32412 ms0Insufficient traffic
19 requests, under the 20 Libra needs
/api/trpc/agentExecutions.getAgentExecutions0 → 60 → 0 (0%)not measured → 12142 ms0Insufficient traffic
6 requests, under the 20 Libra needs
model_chunk Automation Agent (Sol experiment)0 → 00 → 0 (0%)not measured0No traffic
No requests recorded since this deploy.
/api/trpc/agentExecutions.findRecentTrees0 → 00 → 0 (0%)not measured0No traffic
No requests recorded since this deploy.
/api/trpc/agent.list0 → 00 → 0 (0%)not measured0No traffic
No requests recorded since this deploy.
/api/trpc/agent.getInvocationSources0 → 00 → 0 (0%)not measured0No traffic
No requests recorded since this deploy.
/api/trpc/agentExecutions.executeFromClientSend0 → 00 → 0 (0%)not measured0No traffic
No requests recorded since this deploy.
execute_tool list-calendar-events0 → 00 → 0 (0%)not measured0No traffic
No requests recorded since this deploy.

Changed files → surfaces

  • apps/server/src/services/crm/calendar-events.tsexecute_tool list-calendar-events
  • apps/mail/modules/conversations/components/ConversationSearchCommandBar.tsxno production surface mapped
  • apps/mail/modules/conversations/components/OverviewDocTab.tsxno production surface mapped
  • apps/server/.claude/skills/playbook-instruction-eval/eval-domain-identification.tsno production surface mapped
  • apps/server/.claude/skills/playbook-instruction-eval/eval-vendor-bucket.tsno production surface mapped
  • apps/server/.claude/skills/playbook-instruction-eval/fixtures/reza-fathom-vendor-bucket-meeting.txtno production surface mapped
  • apps/server/.claude/skills/playbook-instruction-eval/fixtures/vooma-mcleod-meeting.txtno production surface mapped
  • apps/server/docs/design/ambiguous-meeting-company-assignment.mdno production surface mapped
  • apps/server/src/db/crm-schema.tsno production surface mapped
  • apps/server/src/db/migrations/add_ambiguity_pinged_at_to_crm_meeting_events.sqlno production surface mapped
  • apps/server/src/mastra/skills/crm/domain-identification.tsno production surface mapped
  • apps/server/src/mastra/workflows/event-execution/pre-execution-setup.tsno production surface mapped
  • apps/server/src/services/crm/__tests__/meeting-ambiguity-ping-message.test.tsno production surface mapped
  • apps/server/src/services/crm/__tests__/meeting-ambiguity-ping.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__/meeting-events.markMeetingAsFailed.test.tsno production surface mapped
  • apps/server/src/services/crm/__tests__/meeting-events.shouldUpdateEventConversationId.test.tsno production surface mapped
  • apps/server/src/services/crm/meeting-ambiguity-ping-message.tsno production surface mapped
  • apps/server/src/services/crm/meeting-ambiguity-ping.tsno production surface mapped
  • apps/server/src/services/crm/meeting-events.tsno production surface mapped