# meeting-transcription Specification ## Purpose TBD - created by archiving change define-meeting-assistant-v1. Update Purpose after archive. ## Requirements ### Requirement: Meeting Assistant transcribes meetings with speaker attribution Meeting Assistant SHALL transcribe meeting audio and associate transcript segments with speakers when speaker attribution is available. The transcription contract SHALL remain independent of the meeting source so that local audio, in-person meetings, and future meeting-platform integrations can use the same downstream processing. #### Scenario: Speaker-attributed transcript is produced - **WHEN** meeting audio is transcribed and speaker attribution is available - **THEN** the transcript contains ordered speaker-attributed segments #### Scenario: Speaker attribution is unavailable - **WHEN** meeting audio is transcribed but speaker attribution is unavailable - **THEN** Meeting Assistant keeps the transcript and marks speaker identity as unknown rather than discarding the meeting ### Requirement: Speech recognition pipelines are streamable and configurable Meeting Assistant SHALL route audio through a provider-independent speech recognition pipeline contract. The configured pipeline SHALL be selected through DI from the ASR backend configuration and SHALL expose operations to initialize, wait for readiness, write captured audio chunks, read live transcript segments, and read the finished transcript after capture completes. Reading the finished transcript SHALL accept optional pipeline options, including a requested speaker count hint. For real meeting recordings, Meeting Assistant SHALL derive the requested speaker count hint from the meeting note `attendees` frontmatter when at least two attendees are listed. The configured pipeline SHALL receive audio chunks as they are captured rather than requiring the recording coordinator to finish a complete meeting audio file first. #### Scenario: Streaming provider receives live audio - **WHEN** recording mode is active and the combined audio source emits a chunk - **THEN** Meeting Assistant can pass that chunk to the configured speech recognition pipeline before the audio source completes #### Scenario: Pipeline readiness is separated from recording capture - **WHEN** recording mode starts before the configured speech recognition backend is ready - **THEN** Meeting Assistant starts audio capture immediately and lets the pipeline finish readiness before it emits live transcript output #### Scenario: Pipeline is changed by configuration - **WHEN** the configured ASR backend setting is changed - **THEN** Meeting Assistant selects the configured speech recognition pipeline without changing recording control or transcript persistence code #### Scenario: ASR backend is tested with a WAV file - **WHEN** the user submits a local PCM WAV file to the ASR diagnostic endpoint - **THEN** Meeting Assistant streams that file through the configured speech recognition pipeline and returns the emitted live transcript segments #### Scenario: ASR diagnostic backend is unavailable - **WHEN** the configured speech recognition pipeline fails while processing the diagnostic WAV file - **THEN** Meeting Assistant returns a diagnostic backend failure response instead of hiding the pipeline error #### Scenario: Final diarization backend is tested with a WAV file - **WHEN** the user submits a local PCM WAV file to the final diarization diagnostic endpoint - **THEN** Meeting Assistant streams the file through the configured speech recognition pipeline and returns the finished transcript segments #### Scenario: Final diarization receives a speaker count hint - **WHEN** the user submits a local PCM WAV file and `numSpeakers` to the final diarization diagnostic endpoint - **THEN** Meeting Assistant passes the requested speaker count to the configured speech recognition pipeline as a finished-transcript option #### Scenario: Meeting attendee count provides final speaker hint - **WHEN** a meeting note has two or more entries in `attendees` - **THEN** Meeting Assistant passes that attendee count to the configured speech recognition pipeline as the finished-transcript speaker count hint #### Scenario: Empty or single-attendee meeting note has no final speaker hint - **WHEN** a meeting note has zero or one entry in `attendees` - **THEN** Meeting Assistant leaves the finished-transcript speaker count hint unset ### Requirement: Version 1 keeps local Whisper transcription available Meeting Assistant SHALL provide a local Whisper speech recognition pipeline as a fallback ASR backend for version 1. The local Whisper adapter SHALL use configurable model path, language, and streaming window settings. When local Whisper is the configured speech recognition pipeline, Meeting Assistant SHALL be able to run a final pyannote diarization pass over the temporary mixed audio and either assign pyannote speaker turns back to live Whisper transcript segments by timestamp overlap or build finished transcript segments from pyannote speaker turns. The pyannote finalization backend SHALL allow configuration to use either pyannote's normal `speaker_diarization` annotation or pyannote's `exclusive_speaker_diarization` annotation. When a speaker count hint is provided through the speech recognition pipeline options, the pyannote finalization backend SHALL pass it to pyannote as `num_speakers`. The pyannote finalization backend SHALL use a reusable runtime image for Python dependencies and a persistent configured cache folder for Hugging Face models, torch artifacts, and pip artifacts so repeated finalization runs do not redownload the diarization model or reinstall the full Python dependency stack. #### Scenario: Local Whisper model is configured - **WHEN** the configured provider is `whisper-local` - **THEN** Meeting Assistant uses the configured local Whisper model path to transcribe incoming audio windows #### Scenario: Local Whisper transcript is finalized with pyannote speaker turns - **WHEN** the configured provider is `whisper-local`, live Whisper transcript segments exist, and pyannote returns speaker turns for the temporary recording - **THEN** Meeting Assistant rewrites the transcript with the original Whisper text and the best-overlap pyannote speaker label for each segment #### Scenario: Local Whisper transcript is segmented by pyannote speaker turns - **WHEN** the configured provider is `whisper-local`, pyannote turn alignment is configured, live Whisper transcript segments exist, and pyannote returns speaker turns for the temporary recording - **THEN** Meeting Assistant rewrites the transcript as ordered pyannote speaker-turn segments with matching Whisper text assigned to each turn #### Scenario: Local Whisper uses exclusive pyannote diarization - **WHEN** the configured provider is `whisper-local` and exclusive pyannote annotation is configured - **THEN** Meeting Assistant reads speaker turns from pyannote's `exclusive_speaker_diarization` output #### Scenario: Local Whisper pyannote runtime cache is reused - **WHEN** local Whisper finalization runs pyannote more than once - **THEN** Meeting Assistant uses the configured reusable pyannote runtime image and persistent pyannote model cache instead of treating each run as a clean download environment #### Scenario: Local Whisper pyannote diarization is unavailable - **WHEN** the configured provider is `whisper-local` but pyannote is disabled, has no configured token, or returns no speaker turns - **THEN** Meeting Assistant keeps the live Whisper transcript instead of deleting it ### Requirement: Azure Speech can provide streaming transcription Meeting Assistant SHALL provide an Azure Speech speech recognition pipeline that streams captured PCM audio to Azure AI Speech through the Azure Speech SDK conversation transcription API. The Azure Speech adapter SHALL use configurable endpoint, region, language, key, and key environment variable settings. The Azure Speech adapter SHALL support configurable diarization of intermediate conversation transcription results. The Azure Speech adapter SHALL bound recognizer shutdown after the input audio stream is closed. When Azure Speech is the configured speech recognition pipeline, Meeting Assistant SHALL emit live transcript segments from Azure conversation transcription events with Azure speaker IDs when available. When Azure Speech is the configured speech recognition pipeline, Meeting Assistant SHALL use the live Azure conversation transcription segments as the finished transcript without running pyannote finalization. #### Scenario: Azure Speech pipeline is configured - **WHEN** the configured provider is `azure-speech` - **THEN** Meeting Assistant streams captured PCM chunks to Azure AI Speech conversation transcription through the configured speech recognition pipeline without changing recording control or transcript persistence code #### Scenario: Azure Speech returns speaker IDs - **WHEN** Azure Speech conversation transcription returns transcript text with speaker identity - **THEN** Meeting Assistant emits ordered transcript segments with those Azure speaker identities #### Scenario: Azure Speech key is resolved from environment - **WHEN** Azure Speech is configured with `KeyEnv` - **THEN** Meeting Assistant reads the Azure Speech key from that environment variable #### Scenario: Azure Speech stream shutdown is bounded - **WHEN** the captured audio stream has ended - **THEN** Meeting Assistant stops Azure Speech continuous recognition within the configured stop timeout instead of waiting indefinitely #### Scenario: Azure Speech transcript is finalized from live conversation segments - **WHEN** the configured provider is `azure-speech` and live Azure transcript segments exist - **THEN** Meeting Assistant uses the live Azure conversation transcript segments as the finished transcript ### Requirement: FunASR can provide speaker-attributed streaming transcription Meeting Assistant SHALL provide a FunASR speech recognition pipeline that streams PCM audio to a configured FunASR WebSocket endpoint. The FunASR provider SHALL preserve speaker attribution from FunASR response fields when they are present and SHALL fall back to `Unknown` when no speaker field is returned. Meeting Assistant SHALL write the mixed recording audio only as a temporary processing artifact while streaming transcription is active. After capture stops and the streaming provider has drained already-captured audio, Meeting Assistant SHALL be able to run a final FunASR Python `AutoModel` pass with VAD, punctuation, and CAMPPlus speaker modeling over the temporary audio. When that final pass returns sentence-level speaker labels, Meeting Assistant SHALL rewrite the transcript markdown with the final speaker-attributed sentence segments. Meeting Assistant SHALL delete temporary recording audio after the run completes, whether final diarization succeeds, fails, or produces no speaker output. On application startup, Meeting Assistant SHALL delete stale temporary recording audio left behind by previous interrupted runs. When configured, Meeting Assistant SHALL manage a local FunASR backend lifecycle for the FunASR provider by starting a non-blocking backend warm-up when the application starts, installing the configured streaming runtime image, preparing the mounted model and hotword folder, starting the backend, keeping the container alive for the backgrounded FunASR server process, and stopping it when the application stops. Recording SHALL be able to start while the backend is still warming up; captured audio SHALL be queued and streamed once the backend is healthy. Backend installation, startup commands, and WebSocket readiness SHALL be bounded by configurable timeouts so startup failures are diagnosable. #### Scenario: FunASR pipeline is configured - **WHEN** the configured provider is `funasr` - **THEN** Meeting Assistant streams captured PCM chunks to the configured FunASR WebSocket endpoint through the configured speech recognition pipeline without changing recording control or transcript persistence code #### Scenario: FunASR returns speaker fields - **WHEN** FunASR returns transcript text with speaker identity fields - **THEN** Meeting Assistant emits ordered transcript segments with those speaker identities #### Scenario: Final FunASR diarization rewrites transcript - **WHEN** recording stops after mixed audio was temporarily stored and the final FunASR diarization pass returns sentence-level speaker labels - **THEN** Meeting Assistant rewrites the transcript markdown with the final speaker-attributed sentence segments #### Scenario: Final FunASR diarization has no speaker output - **WHEN** recording stops but the final diarization pass is disabled or returns no sentence-level speaker labels - **THEN** Meeting Assistant keeps the live streaming transcript instead of deleting it #### Scenario: Temporary recording audio is deleted after completion - **WHEN** a recording run completes after using temporary audio for final processing - **THEN** Meeting Assistant deletes the temporary audio file from disk #### Scenario: Stale temporary recordings are deleted on startup - **WHEN** Meeting Assistant starts and stale temporary recording files exist from a previous interrupted run - **THEN** Meeting Assistant deletes those stale temporary recording files before accepting new recordings #### Scenario: Managed FunASR backend starts with the application - **WHEN** the configured provider is `funasr` and managed backend startup is enabled - **THEN** Meeting Assistant begins installing the configured FunASR backend image and starting the two-pass streaming backend without blocking recording startup #### Scenario: Recording starts while managed FunASR backend is warming up - **WHEN** recording starts before the managed FunASR backend is ready - **THEN** Meeting Assistant starts audio capture immediately and queues captured audio until the backend WebSocket is healthy #### Scenario: Managed FunASR backend installation stalls - **WHEN** a managed FunASR backend command exceeds its configured timeout - **THEN** Meeting Assistant reports a backend startup timeout instead of waiting indefinitely #### Scenario: Managed FunASR backend WebSocket is not healthy - **WHEN** the managed backend container has started but the FunASR WebSocket endpoint is not accepting sessions - **THEN** Meeting Assistant continues waiting for backend readiness until the configured startup timeout is reached #### Scenario: Managed FunASR backend stops with the application - **WHEN** Meeting Assistant stops after starting a managed FunASR backend - **THEN** Meeting Assistant stops the managed backend process or container ### Requirement: Meeting Assistant learns speaker identities locally Meeting Assistant SHALL maintain a local SQLite speaker identity database in the user's application data folder. The speaker identity database SHALL store speaker identities, optional canonical names, aliases, candidate names, meeting file references, and a bounded set of WAV snippets per identity. Meeting file references SHALL include the meeting note file address and the transcript file address. Meeting Assistant SHALL calculate speaker identity participation counts from meeting file references when needed instead of persisting a denormalized transcript count. Each speaker identity SHALL store a last-modified timestamp used by active-age filtering, and Meeting Assistant SHALL update it whenever the identity is created or modified by identification, candidate updates, snippet changes, reference changes, or merge operations. The configured maximum snippet count per identity SHALL prevent unbounded growth. Live speaker matching SHALL be read-only with respect to the speaker identity database. Candidate elimination, canonical promotion, transcription counters, stored snippet updates, and new unmatched identity creation SHALL happen only after transcription is finished and after automatic summary generation has completed, using the latest meeting note frontmatter. When the summary agent records a speaker override from a diarized transcript label to a named speaker, final speaker identity processing SHALL attach the current run speaker sample to an existing identity with that name when one exists, or create a new canonical speaker identity with that name when none exists. When a speaker override maps a current-run unnamed candidate to an existing named identity, Meeting Assistant SHALL merge the candidate's meeting reference and useful snippets into the named identity instead of leaving a duplicate candidate. When the summary agent records that a speaker identity was wrongfully matched, final speaker identity processing SHALL delete the matching identity from the local speaker identity database so it cannot be matched again unless it is newly created in the future. #### Scenario: Unknown speaker is learned from meeting attendees - **WHEN** a finished transcript contains an unmatched diarized speaker and the meeting note has attendees - **THEN** Meeting Assistant stores a new unnamed speaker identity with candidate names from the attendees that were not already matched in that meeting - **AND** stores a meeting file reference for that identity #### Scenario: Speaker snippets are bounded - **WHEN** Meeting Assistant adds a snippet for an identity that already has the configured maximum number of snippets - **THEN** Meeting Assistant does not store more snippets for that identity #### Scenario: Identity modification updates active-age timestamp - **WHEN** Meeting Assistant creates, identifies, updates candidates for, stores snippets for, stores references for, or merges a speaker identity - **THEN** Meeting Assistant updates that identity's last-modified timestamp #### Scenario: Final speaker identity learning uses summary-refined attendees - **GIVEN** the summary agent changes meeting note attendees during automatic summary generation - **WHEN** Meeting Assistant performs final speaker identity learning and candidate creation - **THEN** it uses the attendee list from the meeting note after the summary agent changes #### Scenario: Speaker override attaches to existing identity - **GIVEN** the speaker identity database contains canonical speaker `Sabrina` - **AND** the summary agent records that transcript speaker `Guest-01` is `Sabrina` - **WHEN** final speaker identity processing runs - **THEN** Meeting Assistant stores the meeting reference and current speaker sample on Sabrina's identity - **AND** does not create a separate unnamed candidate for `Guest-01` #### Scenario: Speaker override creates named identity - **GIVEN** the speaker identity database has no accepted name `Sabrina` - **AND** the summary agent records that transcript speaker `Guest-01` is `Sabrina` - **WHEN** final speaker identity processing runs - **THEN** Meeting Assistant creates a canonical speaker identity named `Sabrina` - **AND** stores the meeting reference and current speaker sample on that identity #### Scenario: Speaker identity deletion removes a wrong match - **GIVEN** the speaker identity database contains canonical speaker `Sabrina` - **AND** the summary agent records that `Sabrina` was wrongfully matched - **WHEN** final speaker identity processing runs - **THEN** Meeting Assistant removes Sabrina's identity from the speaker identity database - **AND** the relabeled transcript uses `Removed-1` instead of `Sabrina` ### Requirement: Speaker identities can be merged diagnostically Meeting Assistant SHALL expose a diagnostic endpoint that merges duplicate speaker identities. The merge process SHALL compare recently-created identities, using a configurable recent age that defaults to two weeks, against all other identities in matcher batches bounded by the configured batch size. The merge process SHALL require a match and a second validation match using a different source sample before merging two identities. When identities are merged, Meeting Assistant SHALL retain one identity, move useful names from the merged identity into aliases, combine meeting file references, retain a bounded set of snippets from both identities, and append an audit line to each referenced transcript in the form ` and were merged`. #### Scenario: Recently-created duplicate identity is merged - **GIVEN** a recently-created identity and an older identity have matching speaker snippets - **WHEN** the diagnostic merge endpoint is triggered - **THEN** Meeting Assistant validates the match twice with different source snippets - **AND** merges the recent identity into the older identity - **AND** stores the recent identity name as an alias on the retained identity - **AND** keeps meeting file references from both identities - **AND** appends the merge audit line to the referenced transcripts #### Scenario: Old identities are not used as merge sources - **GIVEN** two identities older than the configured recent age - **WHEN** the diagnostic merge endpoint is triggered - **THEN** Meeting Assistant does not compare them as source identities ### Requirement: Speaker candidates are eliminated across meetings Meeting Assistant SHALL update candidate names for a matched unnamed identity using the intersection of its existing candidate names and the current meeting attendees. Meeting Assistant SHALL treat identity aliases as acceptable names during candidate elimination and when excluding already-matched attendees from new unmatched speaker candidates. When the candidate intersection leaves exactly one candidate, Meeting Assistant SHALL promote that candidate to the canonical speaker name. When an identity receives a canonical name, Meeting Assistant SHALL append an audit line to each referenced transcript in the form ` was identified as `. When the candidate intersection is empty, Meeting Assistant SHALL replace the oldest stored snippet for that identity and reset candidates from the current meeting attendees, because the previous snippet may have been dirty. #### Scenario: Candidate elimination promotes canonical name - **GIVEN** an unnamed identity has candidate names `John` and `Mike` - **WHEN** that identity matches a speaker in a later meeting with attendees `Jane`, `John`, and `Chris` - **THEN** Meeting Assistant removes `Mike` from the identity candidates and promotes `John` as the canonical name - **AND** appends the identity naming audit line to the identity's referenced transcripts #### Scenario: Alias participates in candidate elimination - **GIVEN** an unnamed identity has candidate name `Michael` and alias `Mike` - **WHEN** that identity matches a speaker in a later meeting with attendee `Mike` - **THEN** Meeting Assistant treats `Mike` as matching `Michael` - **AND** promotes `Michael` as the canonical name #### Scenario: Empty candidate intersection resets candidates - **GIVEN** an unnamed identity has candidate names `John` and `Mike` - **WHEN** that identity matches a speaker in a later meeting with attendees `Jane` and `Chris` - **THEN** Meeting Assistant resets the identity candidates to `Jane` and `Chris` - **AND** replaces the oldest stored snippet for that identity with the current speaker snippet ### Requirement: Speaker identity matches relabel transcripts Meeting Assistant SHALL attempt to match unknown diarized speaker snippets against known speaker identities ordered by calculated meeting reference count. Speaker identity matching SHALL use a dedicated Azure Speech diarization verifier component rather than the configured speech recognition pipeline. The matcher SHALL test at most the configured batch size of known people per diarization session and continue with later batches until a match is found or no candidates remain. The matcher SHALL prioritize identities whose canonical name or aliases match current meeting attendees. After attendee-matched identities, the matcher SHALL order identities by calculated meeting reference count, filter out non-attendee identities whose last update is older than the configured active age, and cap the candidate set at the configured maximum match candidate count. When a match is confirmed in final identity processing, Meeting Assistant SHALL store a meeting file reference for that identity. When a match is confirmed and the identity has a canonical name, Meeting Assistant SHALL rewrite finished transcript segments for that diarized speaker with the canonical name. When a match is confirmed and the matched speaker is not already listed in meeting note attendees by display name or alias, Meeting Assistant SHALL add the speaker display name to the attendee list. When a match is confirmed and the meeting note attendees contain both the speaker display name and one or more accepted aliases for that same speaker, Meeting Assistant SHALL remove the alias attendee entries and keep the display name entry. When Meeting Assistant writes attendees from calendar metadata, it SHALL match attendee display names exactly against known identity canonical names and aliases, replace matches with the identity display name, and deduplicate attendees that map to the same identity. #### Scenario: Finished transcript is relabeled after a confirmed match - **GIVEN** the speaker identity database contains canonical speaker `Chris` - **WHEN** a finished transcript has diarized speaker `Guest03` and the matching backend confirms it is `Chris` - **THEN** Meeting Assistant rewrites `Guest03` segments in the transcript as `Chris` #### Scenario: Confirmed match stores meeting reference - **GIVEN** the speaker identity database contains canonical speaker `Chris` - **WHEN** a finished transcript has diarized speaker `Guest03` and the matching backend confirms it is `Chris` - **THEN** Meeting Assistant stores the meeting note and transcript file addresses as a reference for `Chris` #### Scenario: Confirmed match removes duplicate aliases - **GIVEN** the speaker identity database contains canonical speaker `Christopher` with alias `Chris` - **AND** the meeting note attendees contain both `Christopher` and `Chris ` - **WHEN** live or final speaker matching confirms a diarized speaker is `Christopher` - **THEN** Meeting Assistant keeps `Christopher` in the meeting note attendees - **AND** removes `Chris ` from the meeting note attendees ### Requirement: Speaker matching runs during active transcription Meeting Assistant SHALL start speaker identity matching only after the configured initial transcription duration has elapsed. For backends that emit live diarized transcript segments, Meeting Assistant SHALL keep a bounded in-memory sliding audio buffer with chunk timestamps and extract candidate WAV snippets from that buffer when live diarized segments arrive. Meeting Assistant SHALL keep only the configured best candidate snippets per diarized speaker in memory. Better snippets SHALL be preferred when the segment looks like a continuous medium-length sentence. Meeting Assistant SHALL periodically match unresolved diarized speaker samples while transcription is active and attempt to match them against the local identity database. Meeting Assistant SHALL run live matching incrementally at the configured interval only when at least one new unmapped diarized speaker sample appears or the meeting note attendee frontmatter changes while unmapped speaker samples still exist. When a speaker is matched during transcription, Meeting Assistant SHALL rewrite already-written live transcript segments for that diarized speaker and write future transcript segments using the canonical name. Live speaker matching SHALL be read-only with respect to the speaker identity database. Candidate elimination, canonical promotion, transcription counters, stored snippet updates, and new unmatched identity creation SHALL happen only after transcription is finished and after automatic summary generation has completed, using the latest meeting note frontmatter. For backends that only provide diarization after finalization, Meeting Assistant SHALL defer speaker identity matching until finished diarization is available, extract candidate snippets from the completed temporary recording, complete identity matching, and only then allow summary generation to start. #### Scenario: Matching waits for useful speech duration - **WHEN** transcription has been active for less than the configured speaker identification initial delay - **THEN** Meeting Assistant does not run speaker identity matching yet #### Scenario: Live matching uses in-memory speaker samples - **WHEN** a live diarized transcript segment identifies an unresolved speaker - **THEN** Meeting Assistant extracts a WAV snippet for that segment from the in-memory sliding audio buffer - **AND** uses retained speaker samples for live identity matching without reading the temporary recording file #### Scenario: Live match rewrites current and future transcript writes - **WHEN** periodic matching confirms that diarized speaker `Guest03` is canonical speaker `Chris` - **THEN** already-written live transcript segments for `Guest03` are rewritten as `Chris` - **AND** later live transcript segments for `Guest03` are written as `Chris` #### Scenario: New live speaker triggers another identification round - **GIVEN** live matching already checked the current unresolved speaker samples - **WHEN** a new unmapped diarized speaker sample appears - **THEN** Meeting Assistant runs another live matching round at the next configured interval #### Scenario: Attendee changes trigger another identification round - **GIVEN** live matching already checked unresolved speaker samples - **WHEN** the meeting note attendee frontmatter changes - **THEN** Meeting Assistant runs another live matching round at the next configured interval using the latest attendees #### Scenario: Live matching does not make final identity decisions - **WHEN** live matching finds a possible speaker identity during transcription - **THEN** Meeting Assistant does not change candidate names, promote canonical names, increment counters, store snippets, or create unmatched identities - **AND** the final speaker identity pass uses the latest meeting note attendees after transcription finishes ### Requirement: Speech recognition diagnostics are launch-profile aware Meeting Assistant SHALL preserve existing ASR diagnostic endpoint URLs as default-profile URLs. Meeting Assistant SHALL provide equivalent named-profile ASR diagnostic endpoint URLs that include the profile name. When a named-profile ASR diagnostic endpoint is used, Meeting Assistant SHALL create the speech recognition pipeline from the resolved profile configuration. #### Scenario: Default ASR diagnostic URL uses default profile - **WHEN** the user submits a WAV file to the existing ASR diagnostic endpoint URL - **THEN** Meeting Assistant streams the WAV through the default launch profile's configured speech recognition pipeline #### Scenario: Named ASR diagnostic URL uses named profile - **WHEN** the user submits a WAV file to the ASR diagnostic endpoint URL for profile `english` - **THEN** Meeting Assistant streams the WAV through the resolved `english` launch profile's configured speech recognition pipeline ### Requirement: Azure Speech post-stream refinement is configurable Meeting Assistant SHALL allow the Azure Speech streaming transcription provider to configure the Speech SDK post-processing option. When configured for a compatible Azure Speech SDK recognizer, Meeting Assistant SHALL set `SpeechServiceResponse_PostProcessingOption` on the Azure Speech SDK `SpeechConfig`. The default application configuration SHALL keep `PostProcessingOption` empty while using live `ConversationTranscriber` and SHALL use a region that supports post-stream refinement for future compatible recognizer paths. Meeting Assistant SHALL keep using the existing Azure live streaming transcription flow rather than switching to batch or offline processing. When the Azure live streaming backend uses `ConversationTranscriber`, Meeting Assistant SHALL prefer working live diarized transcription over applying a post-processing option that is only documented for `SpeechRecognizer`. #### Scenario: Azure Speech post-refinement is configured - **WHEN** `AzureSpeech.PostProcessingOption` is `PostRefinement` - **AND** the configured Azure Speech recognizer supports SDK post-processing - **THEN** Meeting Assistant sets `SpeechServiceResponse_PostProcessingOption` to `PostRefinement` on the Azure Speech SDK configuration #### Scenario: Azure live conversation transcription remains available - **WHEN** `AzureSpeech.PostProcessingOption` is `PostRefinement` - **AND** the Azure backend is using live `ConversationTranscriber` - **THEN** Meeting Assistant skips the post-processing SDK property - **AND** continues using live conversation transcription #### Scenario: Azure Speech post-processing is unset - **WHEN** `AzureSpeech.PostProcessingOption` is empty - **THEN** Meeting Assistant does not set a post-processing option on the Azure Speech SDK configuration ### Requirement: Speech recognition can be restarted within one meeting Meeting Assistant SHALL support replacing the active speech recognition pipeline for a recording run while preserving the run's meeting artifacts and transcript session. The replacement speech recognition pipeline SHALL use the target launch profile's resolved transcription options. #### Scenario: Replacement pipeline uses target profile options - **GIVEN** a recording run started with one launch profile - **WHEN** the active transcription profile is switched to another launch profile - **THEN** the new speech recognition pipeline is created from the target profile options - **AND** it receives audio captured after the switch request and audio buffered during the switch ### Requirement: Speaker identity samples require uninterrupted speech Meeting Assistant SHALL only retain speaker identity samples after a diarized speaker has produced at least the configured minimum duration of uninterrupted speech. The default minimum uninterrupted speech duration SHALL be 30 seconds. Meeting Assistant MAY combine adjacent transcript segments for the same diarized speaker into one sample span when no different diarized speaker interrupts the span. When a different diarized speaker appears before the configured minimum duration is reached, Meeting Assistant SHALL discard the pending sample span for the previous speaker. #### Scenario: Short speaker span is not retained - **GIVEN** the configured minimum sample duration is 30 seconds - **WHEN** a diarized speaker produces only 20 seconds of uninterrupted speech - **THEN** Meeting Assistant does not retain a speaker identity sample for that span #### Scenario: Adjacent same-speaker segments form a sample - **GIVEN** the configured minimum sample duration is 30 seconds - **WHEN** a diarized speaker produces adjacent segments totaling at least 30 seconds without another speaker interrupting - **THEN** Meeting Assistant retains one speaker identity sample covering the continuous span #### Scenario: Different speaker interrupts pending span - **GIVEN** the configured minimum sample duration is 30 seconds - **WHEN** `Guest01` speaks for 20 seconds and then `Guest02` speaks - **THEN** Meeting Assistant discards the pending `Guest01` span instead of retaining or later extending it ### Requirement: Speaker identity matching can use pyannote secondary validation Meeting Assistant SHALL support an optional configurable pyannote secondary validation layer for speaker identity matching. When pyannote secondary validation is enabled, Meeting Assistant SHALL verify candidate speaker samples before retaining them for identity matching. Samples that pyannote reports as containing multiple speakers SHALL be rejected. When pyannote secondary validation is enabled and the primary identity matcher confirms a speaker, Meeting Assistant SHALL run a second validation pass through pyannote before accepting the match. If pyannote secondary validation cannot confirm that the unknown live sample and matched identity samples belong to one speaker, Meeting Assistant SHALL reject the match. When pyannote secondary validation is disabled, Meeting Assistant SHALL preserve the primary identity matching behavior. #### Scenario: Multi-speaker sample is rejected - **GIVEN** pyannote secondary validation is enabled - **WHEN** pyannote reports multiple speakers in a candidate sample - **THEN** Meeting Assistant does not retain that sample for identity matching #### Scenario: Pyannote rejects primary match - **GIVEN** pyannote secondary validation is enabled - **AND** the primary identity matcher confirms `Guest03` as `Chris` - **WHEN** pyannote reports that the unknown `Guest03` sample and known `Chris` samples contain different speakers - **THEN** Meeting Assistant rejects the match #### Scenario: Disabled pyannote validation preserves primary match - **GIVEN** pyannote secondary validation is disabled - **WHEN** the primary identity matcher confirms `Guest03` as `Chris` - **THEN** Meeting Assistant accepts the match without running pyannote secondary validation