fix(audio): restore the upstream off-path, gate the AI filter off (v0.5.0 incident)

threadnet.8 broke unmuting in production, both ways. With the filter on, LiveKit
refuses the processor because the room is built without webAudioMix, so no local
track ever carries an AudioContext. With the filter off, the options builder
still emitted processor: undefined and rewrote noiseSuppression - LiveKit copies
every key of audioCaptureDefaults into the getUserMedia constraints, undefined
included, and Safari stopped unmuting over it.

The off-path is now a conditional spread that produces an object identical to
upstream: no processor key at all, noiseSuppression untouched. Two regression
tests pin this down and were demonstrably red on the old code.

The feature itself is hard-gated off (AI_NOISE_SUPPRESSION_AVAILABLE) until the
webAudioMix decision is made and tested in a two-person call. The gate also
neutralizes clients that enabled the setting before - that state lives in
localStorage and survives every deployment. The settings UI hides behind the
same gate.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Thore Cimbal
2026-08-16 12:00:00 +00:00
co-authored by Claude Opus 4.8
parent d270e0c706
commit dcc86431dc
6 changed files with 139 additions and 15 deletions
+29
View File
@@ -122,6 +122,35 @@ von ~41 auf ~66 MB (gemessen; der Rest sind Source-Maps und die Crypto-/Vision-w
⚠️ **Mobil ungeprueft.** Der Telefontest wurde bewusst ausgesetzt; weil der Filter opt-in
ist, bleibt er auf schwachen Geraeten schlicht aus. Messung und Herleitung: management #0054.
### Vorfall v0.5.0 (2026-08-16): Filter stillgelegt, Tor in `aiNoiseSuppression.ts`
Die erste Fassung (`0.19.2-threadnet.8`) hat in Produktion das **Entmuten gebrochen** -
in beide Richtungen:
- **Filter an:** `LocalAudioTrack.setProcessor()` verlangt einen AudioContext auf dem
Track. Den gibt es nur, wenn der Raum mit `webAudioMix` gebaut wird - und das steht in
`ConnectionFactory.ts` als unerprobtes Upstream-TODO auskommentiert. Folge: *"Audio
context needs to be set on LocalAudioTrack in order to enable processors"*, es wird nie
ein Audio-Track publiziert.
- **Filter aus:** Der Optionen-Bau setzte `processor: undefined` und schrieb
`noiseSuppression` um. LiveKits `mergeObjectWithoutOverwriting` kopiert **jeden**
Schluessel der `audioCaptureDefaults` bis in die `getUserMedia`-Constraints durch -
auch einen mit Wert `undefined`. In Safari brach damit das Entmuten, obwohl der Track
publiziert wurde.
Daraus zwei **stehende Regeln** fuer diesen Fork:
1. **Der Aus-Pfad eines Opt-in-Features muss identisch mit Upstream sein** - auch in der
Objektform. Kein zusaetzlicher Schluessel, auch nicht mit Wert `undefined`, kein
umgeschriebenes Feld. Umgesetzt als bedingtes Spread in `generateRoomOption`
(`ConnectionFactory.ts`), abgesichert durch Regressionstests in
`ECConnectionFactory.test.ts` (die auf dem alten Stand nachweislich rot waren).
2. **Feature-Tor:** `AI_NOISE_SUPPRESSION_AVAILABLE` in `src/livekit/aiNoiseSuppression.ts`
steht auf `false`, bis `webAudioMix` entschieden und im Call zu zweit getestet ist.
Das Tor neutralisiert auch Clients, die die Einstellung bereits aktiviert hatten
(localStorage) - deren Zustand ueberlebt jedes Deployment. Die Bedienelemente in
`SettingsModal.tsx` sind hinter demselben Tor versteckt.
## 6. Produktname im Widget (`.env.production`)
`VITE_PRODUCT_NAME=aXion1337.Chat` in `.env.production` — eine Zeile, keine