Repository navigation
Conversation
Subscribe in the first authenticated WebSocket message and serialize finalized subtitle events with per-session sequence numbers. Batch captions into dedicated Discord threads, suppress mentions, and flush/archive each session independently of minutes generation. Document the wire contract, handshake, lifecycle, and out-of-order speech edge case. Cover authentication, queue failures, completion, session isolation, and Discord rendering with offline tests.
There was a problem hiding this comment.
Reviewed with Gemini, specifically looking for logic soundness and error / failure handling. Things like network relatex problems are all looking good, but there are some UX issues.
Findings:
B. Orphaned Starter Message in Text Channel
• In makeSubtitleAdapter, Discord requires a message to attach a public thread to:
const starter = await parent.send({
content: 'Live subtitles for this meeting.',
allowedMentions: { parse: [] },
});
• User confusion:
• Every meeting started with default options leaves a standalone message: "Live subtitles for this meeting." in the main text
channel.
• When the meeting ends, Misty archives the thread, but the starter message remains in chat forever with no update, no
timestamp, and no link to the final PDF minutes.
• Over a busy week, text channels will accumulate multiple stray "Live subtitles for this meeting." messages.
Voice Channel Text Chat Pitfall
• Discord voice channels now have their own integrated text chats (ChannelType.GuildVoice).
• Discord does not support creating public threads on messages inside voice channel text chats.
• User confusion: If a user joins voice and types /record start in that voice channel's chat, thread creation fails immediately.
Misty immediately sends the ⚠ Live subtitles are unavailable... warning, even though the user did nothing obviously wrong.
Incomplete Subtitle History vs Pristine PDF
• If the WebSocket drops momentarily, the bot's salvage path cleanly recovers and renders the full PDF report over HTTP.
• However, the subtitle thread will post:
Subtitles ended — history may be incomplete.
• User confusion: Users who see both the thread and the PDF might question whether the PDF minutes are also incomplete or
trustworthy, even though the PDF actually has the complete transcript.
Missing Subtitles Info in /record status
• Running /record status reports only the elapsed recording time and voice channel. It does not mention whether subtitles are
running, nor does it link to the subtitle thread for late-joiners.
Suggested Improvements for Better UX
- Clarify the Warning Notice:
• Change:
⚠ Live subtitles are unavailable or incomplete. Recording and minutes are handled separately.
• To:
⚠ Live subtitles have stopped (or could not be started in this channel). Voice recording is still active, and full minutes
will be posted when the meeting ends. - Update the Starter Message on Stop:
• When archiving, edit the starter message in the main channel (e.g., starter.edit({ content: 'Live subtitles for this meeting
(ended).' })) so users know the thread is closed without opening it. - Link Subtitle Thread in /record status:
• Include the subtitle thread link/mention in /record status output if active.
|
Added starter message updating, so that when the thread is archived under that msg, it will say (archived) at the end. |
Mark each subtitle starter archived after successful thread archive, reject recording starts from voice-channel text chat, and link active subtitles from record status. Update recording documentation and cover channel validation, lifecycle visibility, starter ownership, and failed archives with offline tests.
…see audio-to-transcription latency
What this changes
/record startnow posts finalized live subtitles in a dedicated Discord thread by default, withsubtitles:falseto opt out. Subtitles reuse the meeting's authenticated WebSocket, batch every two seconds, and flush an ended marker before archiving their own thread; failures are isolated from recording and minutes generation.Why
Related to #224. Members can follow finalized meeting text live and search retained history through Discord, without repeatedly polling the cumulative transcript.
The API key and
subtitle_events:truetravel together in the first JSON authentication message. Events are sent only after authentication and scope validation. Sequence numbers describe delivery order; speech timestamps sort each batch. Late AWS results can appear below later speech across batches, and posted messages are not edited. The HTTP/PDF transcript retains its existing chronological ordering.Zone
services/meeting·discord-bot·docs·rootThis deliberately spans the meeting service and its Discord consumer because both sides implement the same optional event protocol. Shared recording documentation and README command summaries describe the resulting behavior.
Changed-file justification
services/meeting/src/stt/transcribe.pyservices/meeting/src/sessions.pyservices/meeting/src/api/subtitles.pyservices/meeting/src/api/routers/meetings.pyservices/meeting/tests/test_transcribe.pyservices/meeting/tests/test_subtitles.pyservices/meeting/tests/test_meetings_api.pydiscord-bot/src/commands/record.jsdiscord-bot/src/adapters/discord/recording.jsdiscord-bot/src/meeting/meetingClient.jsdiscord-bot/src/meeting/meetingSurface.jsdiscord-bot/src/meeting/subtitles.jsdiscord-bot/src/adapters/discord/subtitles.jsdiscord-bot/src/adapters/discord/meetingPosts.jsdiscord-bot/src/context.jsdiscord-bot/src/index.jsdiscord-bot/test/meetingClient.test.jsdiscord-bot/test/adapters-discord.test.jsdiscord-bot/test/subtitles.test.jsservices/meeting/docs/API.mdservices/meeting/docs/ARCHITECTURE.mdservices/meeting/README.mddiscord-bot/README.mddocs/MEETING-RECORDING.mdREADME.mdHow to verify
Run from the repository root:
Both service suites, lint, formatting, and whitespace checks passed locally. Python source paths were verified against this checkout. Automated tests use fake sockets/providers and make no AWS, Google, or LLM calls.
On staging:
subtitles:falseand confirm recording/PDF without a subtitle thread.Checklist
stagingand targetingstaging(or this is a deliberatestaging → mainpromotion).uv run ruff check .anduv run ruff format --check .clean — CI gates both on every Python job. Bot ESLint and Prettier also passed.docs/CONTRIBUTING.mdpre-push checklist.Deployment notes
/record startadds the optionalsubtitlesboolean. The bot's existingpreDeployCommandregisters it; confirm exit 0 and both stagingRegistered …lines after deployment.No migrations, environment variables, or new API keys are required. The protocol is opt-in: existing clients still ingest audio without receiving events. A new bot against an older meeting service disables subtitles after the ready timeout while recording continues, so no strict deploy order is required.
Anything you're unsure about
Live Discord/AWS integration has not been exercised. Staging should confirm permissions, recognition latency, retained-history search, and recovery behavior. Docker image checks were unavailable because the local Docker daemon was not running.
Finalized results have no fixed AWS latency bound. Ordering is guaranteed for event delivery and within each Discord batch, not across already-posted batches. Discord operation timeouts are local; a timed-out remote request may still complete, so cleanup is best-effort during outages. No durable queue or replay is added.