Feature: Upload an audio recording, get a private Swedish transcript, discuss it As someone with a recorded meeting on my computer I want to upload it and then ask questions about what was said So that I can find out what was decided, what is open and who owes what # ADR-033, tapir#33. The load-bearing constraint: an uploaded transcript is # PRIVATE content, so it lives in its own RLS-scoped store and never in the # shared public-caption `transcripts` table (ADR-021). Rejection is synchronous, # the audio is never retained, and a failed job ends in a terminal state with a # message — never a spinner (ADR-025). A file with a video track (a Teams meeting # with the camera off is one) is accepted: only its audio is used and the user is # told. Video playback, keyframes and analysis of what is shown remain #28. Background: Given I am a registered user with audio upload enabled Scenario: The upload form states the limits up front When I open the upload page Then I see the size limit and the length limit before I choose a file Scenario: An accepted recording is queued and I am taken to its page When I upload an audio file within the limits Then it is recorded as queued for me And I am redirected to its page Scenario: A recording with no title is named after its file When I upload an audio file and give no title Then its title is the filename without its extension Scenario: A file with a video track is accepted and I am told the video was ignored When I upload a recording that also has a video track Then it is accepted and queued like any other And its page says only its audio was used and the video was discarded And it says so while queued, while transcribing, when done and when failed Scenario: A video file with no audio is rejected for having no audio When I upload a video that has no audio track Then it is rejected with a message that it has no audio And nothing is recorded Scenario: A recording over the length limit is rejected with both lengths When I upload a recording longer than the limit Then it is rejected stating the recording's length and the limit And nothing is recorded Scenario: A file over the size limit is rejected as too large When I upload a file larger than the size limit Then it is rejected with 413 and a message about the limit And nothing is recorded Scenario: Cover art on an audio file is not a video track When I upload an mp3 whose only video stream is attached cover art Then it is treated as audio and no video notice is shown Scenario: A file that is not media is rejected as unreadable When I upload a file ffprobe cannot read Then it is rejected as the file's fault Scenario: A broken ffprobe is reported as our fault, not as a bad file When the prober itself fails Then the upload is an error rather than a rejection of my file Scenario: A recording becomes a transcript and the audio is deleted Given I have uploaded a recording When it has been transcribed Then the transcript is stored privately for me And the audio is deleted Scenario: The endpoint that served a transcript is recorded and shown Given a recording was transcribed by the fallback endpoint When I view its page Then it names the fallback endpoint, because that means the audio left the homelab Scenario: The local endpoint is tried first and the fallback only on failure When the primary transcription endpoint fails for any reason Then the fallback endpoint transcribes it But when the primary succeeds the fallback is never contacted Scenario: A failed transcription ends in a message, never a spinner When every transcription endpoint fails Then the upload is failed with a typed reason And its page shows that reason and stops polling Scenario: A restart fails the jobs it interrupted Given the service restarted while a recording was queued or transcribing When it starts again Then those uploads are failed as interrupted And any audio left in the spool is deleted Scenario: Only one recording is transcribed at a time When several recordings are queued Then they are transcribed one after another Scenario: My uploads are invisible to other users Given another user has uploaded a recording When I ask for it by its id Then I get 404 and no part of its title or text And the chat for it calls no model Scenario: I can ask about my recording and get a grounded answer Given my recording has been transcribed When I ask a question about it Then the answer comes from my transcript through the same chat as a video's Scenario: A long recording is not cut to its first third Given my recording's transcript is longer than the caption default When I ask a question about it Then the transcript is sent with the upload budget, not the 18,000-character caption default # ADR-034, two engines and a local reconciler (off unless TAPIR_RECONCILE_BASE_MODEL is set) Scenario: Two engines transcribe the audio and their disagreements are reconciled Given two-engine transcription is enabled When a recording is transcribed Then both engines run on the same audio, the fast one first And a local model chooses between their disagreements And the endpoint names every engine and the model that took part Scenario: Trivial disagreements never reach the model When the engines differ only by a compound written as one word or two, or by a filler word Then the base reading is kept without a model call Scenario: Differences close together are one decision When two differences are separated by a single shared word Then they are asked about as one dispute Scenario: An unsure or changed reading is flagged for me When the model changes a reading or cannot decide Then the page lists the spot with a minute mark, both readings and which was used And an unsure spot keeps the base reading and is marked uncertain Scenario: The model cannot be a cloud model When the reconciliation model or base engine is not a koala/ or iguana/ alias Then the service refuses to start And a cloud alias that merely contains a local host name is refused too Scenario: One engine failing does not fail the upload When one of the two engines fails Then the other engine's transcript is used And the page says only one engine produced it and why And the cloud fallback is not used Scenario: Only when both local engines fail does the cloud fallback run When both local engines fail and a fallback is configured Then the fallback transcribes it and the row records that it did Scenario: A model that is down gives an unreconciled transcript, not a failed job When the reconciliation model fails three times in a row Then the base transcript is kept unreconciled with the reason And the page says it could not be reconciled Scenario: A cancelled job never reaches the cloud When the job is cancelled during either engine Then neither the second engine nor the cloud fallback is called Scenario: A single-engine transcript claims no second opinion When only one engine was configured Then the page does not mention reconciliation