MeetingRecordingPlayer.tsxfix(meetings): a Circleback recording that plays, on open, with a link that goes to Circleback
Three things stood between a Circleback meeting and its recording.
The recording could not be fetched at all. credentials-encryption derives its
AES key from ORG_CREDENTIALS_ENCRYPTION_KEY || BETTER_AUTH_SECRET, so which
secret a credential was written under is a property of the environment at WRITE
time. Every Circleback MCP token set was written under BETTER_AUTH_SECRET; once
the org key was set, every decrypt died with "Cipher job failed", the call sites
swallowed it into "no credentials", and the player said "No playable recording
for this meeting" with the real cause visible nowhere. Decryption now tries each
configured secret; encryption still writes with the first.
The meeting dialog waited for a press before fetching. That poster exists so a
coaching document with ten moments does not fire ten mints on open against a
Fathom budget of 30/min — an argument that does not apply to one recording the
reader opened to watch. `autoLoad` skips the press without autoplaying.
"Open in Circleback" went to storage.googleapis.com. What ingest stored is a
signed GCS object whose signature dies 24h later; unsigned it answers 403
forever. The page is built from the meeting id instead, and no surface will link
to a raw media file any more.
Webhook-era rows (numeric external_id, all before 2026-07-29) still cannot mint
or link — neither ReadMeetings nor the page route takes a numeric id.
Co-Authored-By: Claude Opus 5 (1M context) <<email>>Sep 5, 2026, 12:21 PMrecording-fence.tsfeat(documents): play the call inside the document, at the moment being discussed
A coaching note the rep cannot check against the tape is an assertion, and until now
checking it meant leaving the document for a provider page that opened at 0:00. A
```moment card now embeds the call positioned at `at:`, every timestamp in its
transcript is a button that seeks the player to that line, and a new ```recording
fence embeds a whole call anywhere in a document.
Playback resolves from `externalId`, never a stored URL. That is not a preference:
Circleback's `recording_url` is a signed link that expired 24h after ingest, so every
Circleback moment's "mandatory recording link" was already a 400, and Fathom's is a
web page rather than media. The id is stable, and the server mints from it per view.
Cedar's own <video> rather than a provider embed, because only an element we own can
be seeked — verified against the live providers rather than assumed. Fathom's
/embed/<token> IS frameable (no X-Frame-Options, no frame-ancestors, unlike /share),
but its component declares only {call, autoplay, shareUrl, displayMode} and nine
probed parameter names changed nothing, so it cannot be positioned; it is kept as the
last rung of the fallback ladder. Fathom's share page does deep-link — ?timestamp=125
comes back as currentTime 125.0 — so links out carry the moment. Gong's iframe takes
exact from/to but demands the viewer be signed into Gong, in an iframe, not in Safari.
Gong gains a media path it never had: /v2/calls/extensive with exposedFields.media,
and the tenant's real call URL read off metaData.url instead of the hardcoded
us-4796 subdomain. Its 403 returns `permission_required`, deliberately not
`unavailable` — the recording exists and Cedar may not fetch it, which is a problem
with a named owner and a one-minute fix, so the player names the
api:calls:read:media-url scope and where a Gong admin grants it. The connection form
now asks for it up front. This lives in gong-recording.ts, not gong.ts, because
gong.ts reaches crm/meeting-events -> meetings/index.ts -> back to gong.ts, and
importing the minter from there crashes on load with a TDZ error.
Nothing mints until the reader presses play: Fathom allows 30 download requests per
60s per key and renders a cold recording in ~34s, so a document with ten moments that
minted eagerly would spend a third of that budget before anyone pressed anything.
Not verified, and called out in the design doc: no live mint ran (this machine's
ORG_CREDENTIALS_ENCRYPTION_KEY does not match those connection rows), the Gong path
has never run against Gong since no org has a connection, and whether a seek lands on
the right words is still unproven — Fathom's call started_at and recording.started_at
differ by 62s on the one sample inspected.
Also carries a pre-existing working-tree change to fathom.ts that could not be
separated from the same file: transcript turns keep their timestamp, so the coaching
rubric's discovery-length row is scoreable for webhook-ingested calls. Its test is
included rather than left orphaned.
Co-Authored-By: Claude Opus 5 (1M context) <<email>>Aug 29, 2026, 3:02 PMRecordingFenceNode.tsxfeat(documents): play the call inside the document, at the moment being discussed
A coaching note the rep cannot check against the tape is an assertion, and until now
checking it meant leaving the document for a provider page that opened at 0:00. A
```moment card now embeds the call positioned at `at:`, every timestamp in its
transcript is a button that seeks the player to that line, and a new ```recording
fence embeds a whole call anywhere in a document.
Playback resolves from `externalId`, never a stored URL. That is not a preference:
Circleback's `recording_url` is a signed link that expired 24h after ingest, so every
Circleback moment's "mandatory recording link" was already a 400, and Fathom's is a
web page rather than media. The id is stable, and the server mints from it per view.
Cedar's own <video> rather than a provider embed, because only an element we own can
be seeked — verified against the live providers rather than assumed. Fathom's
/embed/<token> IS frameable (no X-Frame-Options, no frame-ancestors, unlike /share),
but its component declares only {call, autoplay, shareUrl, displayMode} and nine
probed parameter names changed nothing, so it cannot be positioned; it is kept as the
last rung of the fallback ladder. Fathom's share page does deep-link — ?timestamp=125
comes back as currentTime 125.0 — so links out carry the moment. Gong's iframe takes
exact from/to but demands the viewer be signed into Gong, in an iframe, not in Safari.
Gong gains a media path it never had: /v2/calls/extensive with exposedFields.media,
and the tenant's real call URL read off metaData.url instead of the hardcoded
us-4796 subdomain. Its 403 returns `permission_required`, deliberately not
`unavailable` — the recording exists and Cedar may not fetch it, which is a problem
with a named owner and a one-minute fix, so the player names the
api:calls:read:media-url scope and where a Gong admin grants it. The connection form
now asks for it up front. This lives in gong-recording.ts, not gong.ts, because
gong.ts reaches crm/meeting-events -> meetings/index.ts -> back to gong.ts, and
importing the minter from there crashes on load with a TDZ error.
Nothing mints until the reader presses play: Fathom allows 30 download requests per
60s per key and renders a cold recording in ~34s, so a document with ten moments that
minted eagerly would spend a third of that budget before anyone pressed anything.
Not verified, and called out in the design doc: no live mint ran (this machine's
ORG_CREDENTIALS_ENCRYPTION_KEY does not match those connection rows), the Gong path
has never run against Gong since no org has a connection, and whether a seek lands on
the right words is still unproven — Fathom's call started_at and recording.started_at
differ by 62s on the one sample inspected.
Also carries a pre-existing working-tree change to fathom.ts that could not be
separated from the same file: transcript turns keep their timestamp, so the coaching
rubric's discovery-length row is scoreable for webhook-ingested calls. Its test is
included rather than left orphaned.
Co-Authored-By: Claude Opus 5 (1M context) <<email>>Aug 29, 2026, 3:02 PMtimestamps.tsfeat(documents): play the call inside the document, at the moment being discussed
A coaching note the rep cannot check against the tape is an assertion, and until now
checking it meant leaving the document for a provider page that opened at 0:00. A
```moment card now embeds the call positioned at `at:`, every timestamp in its
transcript is a button that seeks the player to that line, and a new ```recording
fence embeds a whole call anywhere in a document.
Playback resolves from `externalId`, never a stored URL. That is not a preference:
Circleback's `recording_url` is a signed link that expired 24h after ingest, so every
Circleback moment's "mandatory recording link" was already a 400, and Fathom's is a
web page rather than media. The id is stable, and the server mints from it per view.
Cedar's own <video> rather than a provider embed, because only an element we own can
be seeked — verified against the live providers rather than assumed. Fathom's
/embed/<token> IS frameable (no X-Frame-Options, no frame-ancestors, unlike /share),
but its component declares only {call, autoplay, shareUrl, displayMode} and nine
probed parameter names changed nothing, so it cannot be positioned; it is kept as the
last rung of the fallback ladder. Fathom's share page does deep-link — ?timestamp=125
comes back as currentTime 125.0 — so links out carry the moment. Gong's iframe takes
exact from/to but demands the viewer be signed into Gong, in an iframe, not in Safari.
Gong gains a media path it never had: /v2/calls/extensive with exposedFields.media,
and the tenant's real call URL read off metaData.url instead of the hardcoded
us-4796 subdomain. Its 403 returns `permission_required`, deliberately not
`unavailable` — the recording exists and Cedar may not fetch it, which is a problem
with a named owner and a one-minute fix, so the player names the
api:calls:read:media-url scope and where a Gong admin grants it. The connection form
now asks for it up front. This lives in gong-recording.ts, not gong.ts, because
gong.ts reaches crm/meeting-events -> meetings/index.ts -> back to gong.ts, and
importing the minter from there crashes on load with a TDZ error.
Nothing mints until the reader presses play: Fathom allows 30 download requests per
60s per key and renders a cold recording in ~34s, so a document with ten moments that
minted eagerly would spend a third of that budget before anyone pressed anything.
Not verified, and called out in the design doc: no live mint ran (this machine's
ORG_CREDENTIALS_ENCRYPTION_KEY does not match those connection rows), the Gong path
has never run against Gong since no org has a connection, and whether a seek lands on
the right words is still unproven — Fathom's call started_at and recording.started_at
differ by 62s on the one sample inspected.
Also carries a pre-existing working-tree change to fathom.ts that could not be
separated from the same file: transcript turns keep their timestamp, so the coaching
rubric's discovery-length row is scoreable for webhook-ingested calls. Its test is
included rather than left orphaned.
Co-Authored-By: Claude Opus 5 (1M context) <<email>>Aug 29, 2026, 3:02 PMuseRecordingPlayback.tsfeat(documents): play the call inside the document, at the moment being discussed
A coaching note the rep cannot check against the tape is an assertion, and until now
checking it meant leaving the document for a provider page that opened at 0:00. A
```moment card now embeds the call positioned at `at:`, every timestamp in its
transcript is a button that seeks the player to that line, and a new ```recording
fence embeds a whole call anywhere in a document.
Playback resolves from `externalId`, never a stored URL. That is not a preference:
Circleback's `recording_url` is a signed link that expired 24h after ingest, so every
Circleback moment's "mandatory recording link" was already a 400, and Fathom's is a
web page rather than media. The id is stable, and the server mints from it per view.
Cedar's own <video> rather than a provider embed, because only an element we own can
be seeked — verified against the live providers rather than assumed. Fathom's
/embed/<token> IS frameable (no X-Frame-Options, no frame-ancestors, unlike /share),
but its component declares only {call, autoplay, shareUrl, displayMode} and nine
probed parameter names changed nothing, so it cannot be positioned; it is kept as the
last rung of the fallback ladder. Fathom's share page does deep-link — ?timestamp=125
comes back as currentTime 125.0 — so links out carry the moment. Gong's iframe takes
exact from/to but demands the viewer be signed into Gong, in an iframe, not in Safari.
Gong gains a media path it never had: /v2/calls/extensive with exposedFields.media,
and the tenant's real call URL read off metaData.url instead of the hardcoded
us-4796 subdomain. Its 403 returns `permission_required`, deliberately not
`unavailable` — the recording exists and Cedar may not fetch it, which is a problem
with a named owner and a one-minute fix, so the player names the
api:calls:read:media-url scope and where a Gong admin grants it. The connection form
now asks for it up front. This lives in gong-recording.ts, not gong.ts, because
gong.ts reaches crm/meeting-events -> meetings/index.ts -> back to gong.ts, and
importing the minter from there crashes on load with a TDZ error.
Nothing mints until the reader presses play: Fathom allows 30 download requests per
60s per key and renders a cold recording in ~34s, so a document with ten moments that
minted eagerly would spend a third of that budget before anyone pressed anything.
Not verified, and called out in the design doc: no live mint ran (this machine's
ORG_CREDENTIALS_ENCRYPTION_KEY does not match those connection rows), the Gong path
has never run against Gong since no org has a connection, and whether a seek lands on
the right words is still unproven — Fathom's call started_at and recording.started_at
differ by 62s on the one sample inspected.
Also carries a pre-existing working-tree change to fathom.ts that could not be
separated from the same file: transcript turns keep their timestamp, so the coaching
rubric's discovery-length row is scoreable for webhook-ingested calls. Its test is
included rather than left orphaned.
Co-Authored-By: Claude Opus 5 (1M context) <<email>>Aug 29, 2026, 3:02 PM