Libra CodeHub

CedarCopilot/cedar-mail

Branch: staging

fix(meetings): store transcript/recording in S3 during manual sync

merged#2732CedarCopilot

CedarCopilot wants to merge 5 commits into staging from fix/gong-sync-transcript-storage

Live on prod, no production signal yetTimeline and evidence
  1. Opened
    Sep 8, 2026, 2:00 PM
  2. Sep 8, 2026, 4:07 PM
  3. Merged
    Sep 8, 2026, 5:21 PM
  4. Live on prod
    Sep 8, 2026, 5:21 PM
  5. Observed 2 days
    Sep 8, 2026, 5:21 PM
  6. Pipelines steady after this deploy
    Sep 8, 2026, 5:21 PM
  7. Unobserved

    Live on prod, no production signal yet

    Sep 11, 2026, 11:25 AM

Behaviors Libra is checking

If event-execution processing has no valid provider connection or provider ID, it continues creating the meeting event without an S3 transcription key instead of failing the event-ingestion operation.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 11, 2026, 8:23 AM
When the event-execution meeting branch handles a meeting without an existing transcription key, it stores the transcript or recording in S3 at `<connectionId>/<meetingId>.json` before creating or linking the CRM event.Inconclusivemedium confidence

Since deployment, CloudWatch shows 366 [createAndLinkEvents] meeting-branch executions and no occurrences of the tied '[createAndLinkEvents] No ... connection row found ... transcript ... will not be stored in S3.' error. One concrete meeting execution (<id>) logged successful.

prod, checked Sep 11, 2026, 8:25 AM
For newly stored manual-sync meetings, the Turbopuffer upload receives the resolved meeting connection ID instead of an empty connection ID.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 11, 2026, 9:23 AM
If manual sync cannot find the user's provider connection after listing a meeting, it still attaches the meeting without a transcription key and explicitly reports that its transcript or recording was not stored.Inconclusivelow confidence

Unable to complete the required telemetry verification within the available tool loop.

prod, checked Sep 11, 2026, 9:25 AM
When manual sync attaches a meeting, it stores the transcript or recording in S3 at `<connectionId>/<externalId>.json`, assigns that key to the meeting, and completes the storage before creating the CRM event.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 11, 2026, 10:23 AM
The manual-sync response exposes `transcriptStored: false` only when a meeting was attached without a backing S3 blob and omits the field on successful storage, pre-existing keys, unsynced results, and other normal outcomes.Inconclusivehigh confidence

Since 2026-09-09T00:21:17Z in production, targeted OTEL queries found no syncMeetingTool or sync-meeting executions and no transcriptStored response signals. The only initial match was one unrelated workflow-step-queue.CONVERSATION_SYNC_WORKFLOW span; broader sync telemetry was.

prod, checked Sep 11, 2026, 10:25 AM

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

Summary

  • syncMeetingTool.ts (the meetings MCP tool's manual sync action) and mastra/tools/event-execution/createAndLinkEventsTool.ts's meeting branch both call findOrCreateMeetingEvent directly with a driver's meeting data, but never called storeMeetingInS3 or resolved a connectionId , unlike the webhook (meeting-agent.ts) and initial-sync (meeting-steps.ts) paths, which both do this before the event reaches the DB.
  • Net effect: a meeting attached through either of these two paths got a DB row claiming a transcript existed (hasTranscription: true, a real word count) while the S3 blob transcriptionKey should point to was never written , so the UI showed "no transcript available" despite the metadata looking correct. Found and confirmed live against real Gong calls in a customer account (Warp) via both paths.
  • Prior art: a closed, unmerged PR (#2662, "Persist meeting transcripts inside findOrCreateMeetingEvent") investigated this exact bug back on 2026-09-02 and identified the same two call sites. It centralized the fix inside findOrCreateMeetingEvent, but Greptile flagged a real flaw: a failed S3 write there still inserted the row with hasTranscription: true, permanently blocking any later retry (the hydration guard only upgrades a row that doesn't already claim a transcript) , so it was never merged. This PR fixes each call site individually instead and deliberately leaves storeMeetingInS3 unguarded (matching meeting-agent.ts's existing pattern) , a failed write aborts event creation entirely rather than inserting a poisoned row, so a retry of the whole operation tries the S3 write fresh.
  • Both fixes: resolve the caller's connection row for the matched provider, call the driver's best-effort hydrateTranscript (matching meeting-agent.ts's webhook path), then storeMeetingInS3 before findOrCreateMeetingEvent , same order the other two ingestion paths already use.
  • syncMeetingTool.ts also threads the resolved connectionId into its existing Turbopuffer upload call, which previously hardcoded '', and gained a transcriptStored: false output field for the one narrow (traced: TOCTOU-only, not reachable via bad input) case where the connection row can't be found , a thermo-review finding, since that fallback previously returned a fully-successful response with no signal that the transcript wasn't stored.
  • createAndLinkEventsTool.ts doesn't need an equivalent field , it already returns the full meetingEvent DB row via meetingEventResult, so transcriptionKey/hasTranscription are already inspectable.
  • Handles the no-connection-row edge case defensively (logs loudly, still creates the event without a transcript key) rather than crashing the whole attach in both places.

Test plan

  • pnpm --filter @zero/server run types , clean
  • pnpm deps:check , no violations (1786 modules, 9356 dependencies)
  • pnpm --filter @zero/server exec vitest run src/mastra/tools/meetings/__tests__/ src/mastra/tools/event-execution/__tests__/ , 155/155 passing (17 files)
  • 5 new tests in syncMeetingTool.test.ts + 6 new tests in a new createAndLinkEvents-meeting-transcript.test.ts, pinning: S3 write happens before the DB write, an existing transcriptionKey is never overwritten, hydrateTranscript is called and its failure is non-fatal, the event still gets created (without a key) when no connection row is found, and (sync tool only) transcriptStored: false surfaces in that case.
  • Fixed the same bare-barrel-mock issue in 4 test files that broke once these tools started importing isProviderId (which transitively needs MEETING_PROVIDER_IDS from the same mocked @/services/integrations/meetings module)
  • Ran thermo-review on the initial (sync-tool-only) diff , traced every claim to its callee, verified ordering/error-handling parity with meeting-agent.ts, applied its one finding (the transcriptStored signal)
  • Manually backfilled 3 real, already-br
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/__tests__/createAndLinkEvents-meeting-transcript.test.tsno production surface mapped
  • apps/server/src/mastra/tools/event-execution/createAndLinkEventsTool.tsno production surface mapped
  • apps/server/src/mastra/tools/meetings/__tests__/syncMeetingTool.recorder-grant.test.tsno production surface mapped
  • apps/server/src/mastra/tools/meetings/__tests__/syncMeetingTool.test.tsno production surface mapped
  • apps/server/src/mastra/tools/meetings/__tests__/syncMeetingTool.windowing.test.tsno production surface mapped
  • apps/server/src/mastra/tools/meetings/meetingsTool.tsno production surface mapped
  • apps/server/src/mastra/tools/meetings/syncMeetingTool.tsno production surface mapped
  • apps/server/src/services/integrations/meetings/provider-ids.tsno production surface mapped