feat: upload audio, get a private Swedish transcript, discuss it (#33, ADR-033) #39

Merged
mathias merged 299 commits from feat/33-audio-upload-transcript into main 2026-10-03 14:13:37 +00:00
Showing only changes of commit 8f16ab671a - Show all commits
+12 -3
View File
@@ -50,8 +50,11 @@ These caused real mistakes that were caught and corrected; the corrections are l
- **No global cross-tenant *video* table** — videos stay per-user (data-model). Transcripts ARE
shared since ADR-021 (public caption content, keyed by `(provider, provider_video_id)`, non-RLS)
so re-analysis never re-fetches; the *videos* half of cross-tenant dedup stays a Future C concern.
- **No audio-download + speech-to-text in the core path** — captions-first (ADR-007). STT is a
deferred, bounded optional component.
- **No YouTube audio-download + speech-to-text in the core path** — captions-first (ADR-007).
That rejection stands. ADR-033 superseded the STT deferral **for user-supplied audio only**:
an authenticated user may upload a recording and get a *private* transcript (own RLS table,
never the shared `transcripts`). It is off by default (`TAPIR_UPLOADS_ENABLED`) and does not
open the door to scraping audio.
- **No public SaaS / sign-up / billing / Google OAuth verification at scale** — Future C,
deferred behind the Stage 0 gate (ADR-008).
@@ -97,7 +100,7 @@ The repo is **green and shipping** — last tag `v0.15.0`. `task check` passes (
- Adapters present under `internal/adapters/`: `youtube` (captions-first `VideoSource`,
timedtext/InnerTube acquisition per ADR-010), `summarizer` + `llm` (the copied AI router,
now a resilient endpoint chain — local primary → local fallback → external worst-case,
parse-failure-aware, ADR-004 + ADR-022), `store` (Postgres, golang-migrate migrations 001–015),
parse-failure-aware, ADR-004 + ADR-022), `store` (Postgres, golang-migrate migrations 001–017),
`secrets` (file-backed `SecretStore`). The brain HTTP sink (ADR-005) is the remaining
optional sink.
- Stage 1 is open (ADR-012): multi-user with **DB-enforced** isolation — Postgres RLS `FORCE`d
@@ -109,6 +112,12 @@ The repo is **green and shipping** — last tag `v0.15.0`. `task check` passes (
(`TestTranscriptsTableIsSharedNotRLS`). The engine reads stored transcripts before any caption
fetch (`usecase.resolveTranscript`), so re-analysis — re-summarize, paste-a-URL, onboarding
burst — never re-touches YouTube. Per-user summaries/videos stay RLS-scoped.
- Audio upload (ADR-033, migration 017): `internal/uploads` (validation by **probing**, intake,
one-at-a-time in-process worker), `internal/adapters/audio` (ffprobe/ffmpeg), and
`internal/adapters/transcribe` (gateway client + iguana→berget chain). Transcripts are private
in `uploaded_transcripts`; the audio is never retained; chat reuses `chat.Service`. The
isolation test pins the **exact set** of non-RLS tables (`TestRowSecurityOffTablesAreExactlyTheKnownSet`).
The runtime image does not yet contain ffmpeg — that is a pending decision recorded in ADR-033.
- `cmd/tapir` subcommands: `list`, `show`, `auth` (interactive host-side OAuth), `run` (batch
watch→summarize), `serve` (the HTMX+Templ web reader/writer under `internal/web`, a new
transport over the unchanged engine/ports — ADR-003). `tapir env` prints config.