Libra CodeHub

CedarCopilot/cedar-mail

Branch: staging

fix(crm): resolve meeting transcripts by recorded S3 key, not guessed connection IDs

merged#2740CedarCopilot

CedarCopilot wants to merge 1 commit into staging from fix/meeting-transcript-cross-user-connection

Live on prod, 1 of 3 behaviors degradedTimeline and evidence
  1. Opened
    Sep 9, 2026, 9:33 AM
  2. Sep 9, 2026, 9:46 AM
  3. Merged
    Sep 9, 2026, 10:42 AM
  4. Live on prod
    Sep 9, 2026, 10:42 AM
  5. Observed 2 days
    Sep 9, 2026, 10:42 AM
  6. Pipelines steady after this deploy
    Sep 9, 2026, 10:42 AM
  7. Mixed signals

    Live on prod, 1 of 3 behaviors degraded

    Sep 12, 2026, 9:51 AM

Behaviors Libra is checking

Meeting transcript resolution remains backward compatible for older rows without a transcription key, and falls back to connection-ID guessing when a recorded key is absent or does not resolve.Degradedhigh confidence

Post-deploy production telemetry shows 1,457 meeting S3 hydration attempts (worker <id>; API 346) and 1,157 successful direct-key hydrations (worker <id>; API 250), including repeated FULL CONTEXT RESTORED logs. However, the required legacy fallback has no positive evidence: 0.

prod, checked Sep 12, 2026, 9:51 AM
Operators can distinguish whether a meeting transcript was resolved by its authoritative key or by the legacy connection-ID fallback, while missing-content diagnostics include the recorded key and connection IDs tried.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 11, 2026, 9:53 PM
Meeting transcript reads now use the recorded crm_meeting_events.transcription_key, including objects ingested through another user's connection, and check both the primary and fallback meeting buckets before declaring the transcript missing.Not checked
prod, checked Sep 11, 2026, 9:46 PM

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

Summary

  • getEventRawContentByIds and hydrateExecutionEvents (apps/server/src/services/crm/events.ts) resolved a meeting's S3 object by enumerating the querying user's own connection IDs and guessing ${connectionId}/${meetingId}.json for each , a guess that only works when the meeting was ingested under a connection the querying user actually owns.
  • This breaks whenever a meeting is ingested under one user's connection but linked into a different user's deal conversation, which happens routinely for SDR-dialer (Orum) calls linked into an AE's pipeline. Confirmed against real Warp org data: a Gong-recorded Orum call made by rep Manveer, linked into a different rep's (Kendall's) "GeoTek" deal, read as "not found" even though the transcript genuinely exists in S3 under Manveer's connection.
  • crm_meeting_events.transcription_key already holds the exact key set at ingestion time (storeMeetingInS3 writes ${ingestingConnectionId}/${meetingId}.json), but the SELECT in getEventRawContentByIds never read it, so the code had no choice but to guess.
  • Added getMeetingFromS3ByKey (apps/server/src/pipelines.ts) and a shared resolveMeetingS3Object helper (apps/server/src/services/crm/events.ts) that try the recorded key directly first, falling back to the connection-ID-guessing loop (findMeetingInS3) only when no key is on record , this preserves correctness for older rows from before the column was reliably populated.
  • Both call sites (getEventRawContentByIds's meeting branch and hydrateExecutionEvents's meeting branch) now use the shared resolver and log which path resolved the object (direct-key vs guessed-connection) via createStructuredLog, so a regression of this exact bug shows up in structured logs instead of requiring raw SQL forensics to catch.
  • Narrowed findMeetingInS3/getMeetingFromS3ByKey/resolveMeetingS3Object to a new MeetingsBucketEnv type (Pick<CedarRuntimeEnv, 'MEETINGS_BUCKET' | 'MEETINGS_BUCKET_FALLBACK'>) instead of the full runtime env, since that's all they touch , this also makes them cleanly unit-testable with an in-memory bucket instead of a full env fake.

No real customer data was touched , the Warp/Kendall/Manveer case is diagnostic only; the code fix alone makes future reads of that row resolve correctly.

Test plan

  • New regression test apps/server/src/services/crm/__tests__/meeting-s3-cross-user-resolution.test.ts pins the exact cross-user scenario with synthetic ids: a transcriptionKey set to otherUsersConnectionId/meetingId.json, the querying user's own connection IDs excluding that connection, and an S3 mock populated only at the transcriptionKey path , asserts the transcript is still retrieved via direct-key. Also covers: guess-only would have failed (sanity check on the fixture), fallback-to-guessing still works when no key is recorded, and a clean null when neither path resolves.
  • pnpm --filter @zero/server exec vitest run src/services/crm/__tests__/meeting-s3-cross-user-resolution.test.ts , 4/4 passed
  • Full related-suite regression run (12 files, 119 tests) covering other consumers of findMeetingInS3/conversation hydration , all passed
  • pnpm --filter @zero/server run types , clean
  • eslint on touched files , clean

šŸ¤– Generated with Claude Code

https://claude.ai/code/session_01YZB5RgfxHLmuKtWof2wP4g

Greptile Summary

This PR adds authoritative meeting-transcript lookup by the recorded S3 key, retains connection-ID guessing for legacy rows, wires the resolver into raw-content and execution hydration, and adds focused resolver tests and structured resolution logging. The recorded-key approach fixes legitimate cross-user conversation reads, but using a client-writable key directly introduces an object-level authorization gap.

  • Adds getMeetingFromS3ByKey with primary/fallback bucket support.
  • Adds a shared resolver that reports direct
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/pipelines.tsno production surface mapped
  • apps/server/src/services/crm/__tests__/meeting-s3-cross-user-resolution.test.tsno production surface mapped
  • apps/server/src/services/crm/events.tsno production surface mapped