CedarCopilot wants to merge 2 commits into staging from fix/meeting-transcript-hydrate
Live on prod, no production signal yet
Verification reached its 130s outer budget before the verifier returned. Libra is keeping this intent verifying until the next check.
Verification reached its 130s outer budget before the verifier returned. Libra is keeping this intent verifying until the next check.
Unable to complete verification: telemetry shows 47 handle_execute_meeting spans across 23 users with no recorded exceptions, but the hydration log query was not reliably scoped (it returned millions of likely false matches), and available logs do not establish that complete.
Libra has verdicts on 0 of 3 tracked behaviors on prod; 3 are still being checked. Libra checks hourly for 3 days after each deploy.
Meeting-notes providers re-send the same externalId repeatedly, and the early deliveries are often empty , the recorder registers the meeting when its notetaker joins, then attaches the transcript minutes after the call ends.
findOrCreateMeetingEvent's existing-row branch updated conversationId and returned:
if (existingRow) {
eventId = existingRow.meetingEvent.eventId;
if (existingRow.event.conversationId !== conversationId) { … }
return { eventId, isNew: false, meetingEvent: existingRow.meetingEvent };
}
Nothing else was ever written. The first delivery won permanently, and every later payload carrying the real transcript was silently dropped.
Observed on Fellow, 2026-08-26, from the API logs (payload sizes are the S3 writes of each POST):
T7Ptbz1cHb 16:15 → 17:00 1,177 bytes empty shell, ~14 deliveries
17:05 → 17:30 31,866 bytes full transcript, 7 deliveries , all discarded
gCGQPMqXOC 16:45 → 17:25 1,669 bytes empty shell
18:10 → 18:35 51,330 bytes full transcript , all discarded
Fellow finished the transcript at 17:04:06, four minutes after a meeting that ended at 17:00. Cedar received it seven times and stored none of it.
Every one of that user's 135 meeting rows had updated_at == created_at , nothing had ever been updated after creation. 21 were left permanently transcript-less. Downstream, the wiki-maintainer was staging proposals off meetings with no content, which is how the customer noticed.
storeMeetingInS3 writes every re-delivery, and transcriptionKey is just ${connectionId}/${meetingId}.json. 18 of those 21 have a full transcript in S3 today (19,317,60,056 chars). Only the row's pointer and flags were missing, because this branch never wrote them. Repair is a metadata update, not a re-fetch.
Hydrate on re-delivery, upgrade-only: a field is written only when the incoming payload has it and the stored row does not.
That direction is load-bearing. Empty and full deliveries interleave, so an unconditional overwrite would let a late empty payload erase a transcript already held. Covers transcript, ai notes, notes, recording and duration, and returns the updated row rather than the stale one the caller would otherwise get.
Provider-agnostic on purpose. Fellow surfaced it, but any recorder that re-sends the same externalId , Granola, Circleback, Fathom , hits this same branch.
pnpm --filter @zero/server run types cleanpnpm deps:check clean (1572 modules, no violations)The upstream Superglue tool (fellow-meetings-webhook-sync) now rejects transcript-less payloads in buildMeetings, so the pre-meeting shells no longer reach Cedar at all. That was applied and verified separately against live Fellow data. For Fellow this PR is now belt-and-braces; for every other recorder it is the fix.
🤖 Generated with Claude Code
This PR hydrates existing meeting-event rows when later provider deliveries contain transcript, notes, recording, or duration data, while avoiding replacement of already-present content.
The partial-metadata hydration defect should be fixed before merging because later deliveries can still leave transcript or recording fields permanently absent.
The new branch repairs completely
Libra has not measured any production surfaces for this change yet.