feat(server): limites et erreurs 413/422/504, validation WAV (T-017) #17
Loading…
Reference in a new issue
No description provided.
Delete branch "feat/T-017-server-dictate-limits"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Tâche
T-xxx — (titre dans docs/STATUS.md)
Changements
Contrat modifié ?
packages/contract/supabase/migrations—docs/API.md/docs/DATA.mdmis à jour, labelcontract, approbation humaine requiseTests
scripts/check.shvertImpacts cross-domaine
Fichiers hors périmètre de l'agent (et pourquoi) :
Suivi
docs/STATUS.mdmis à jour (ligne de la tâche)docs/CHANGELOG.md: entrée ajoutée sous Unreleaseddocs/DECISIONS.md/docs/ECARTS.mdmis à jour si décision ou écartqa-regression — verdict : APPROVE
Périmètre : uniquement apps/server/** (api/routes.py, api/wav.py nouveau, main.py, providers/stt/base.py+openrouter.py, tests/*) + docs/API.md, docs/CHANGELOG.md, docs/STATUS.md. Aucun fichier hors périmètre backend, rien à signaler en Impacts cross-domaine.
Contrats : packages/contract/openapi.json non modifié (vérifié : seuls 200/422 y étaient déjà déclarés pour /v1/dictate, les
detailde HTTPException n'y sont pas typés) ; supabase/migrations non touché. docs/API.md (ligne "Erreurs :") ne fait que préciser les valeurs dedetailpour des codes 413/422/502/504 déjà documentés avant cette PR — pas une PR contract/*, pas d'approbation humaine requise.Sécurité :
validate_wav) : parseur RIFF/WAVE en lecture seule sur les bytes déjà en mémoire, aucune écriture disque, valide PCM 16 bits/16 kHz/mono comme documenté API.md.MultiPartParser.spool_max_size(Starlette, 1 Mo par défaut) relevé au-delà demax_audio_bytes, sinon toute dictée > ~31 s était spoolée sur disque en violation de la règle CLAUDE.md "jamais écrire l'audio sur disque". Vérifié manuellement (hors tests unitaires) : un WAV de ~1.8 Mo ne crée plus aucun fichier temporaire alors qu'il en créait un avant le correctif.Robustesse :
httpx.TimeoutExceptioncatché avanthttpx.HTTPErrorgénérique (sous-classe) →STTTimeoutErrordistinct./v1/dictateet/v1/agentmappentSTTTimeoutError→ 504 avantSTTError→ 502 (ordre des except respecté, sous-classe en premier).limit_upload_size) : défense en profondeur sur Content-Length, ne bloque pas les requêtes sans cet en-tête (chunked) —_read_audioreste le filet de sécurité, comportement documenté en commentaire.Cross-platform : N/A (backend seul).
Tests :
scripts/check.sh --serververt, 145 passed (123 avant), 1 skipped (intégration OpenRouter opt-in inchangée). Une branche par cas : WAV absent/tronqué/mauvais magic/mauvais nombre de canaux/mauvaise fréquence/mauvaise profondeur/format non-PCM/padding de chunk impair (tests/test_wav.py, 13 tests) ; 413 au niveau route avec limite réduite (tests/test_dictate.py::test_dictate_audio_too_large_is_413) ; 504 distinct de 502 au niveau route et provider (test_dictate_stt_timeout_is_504, test_timeout_raises_stt_timeout_error, test_connect_timeout_also_raises_stt_timeout_error) ; middleware testé en isolation sans transfert réseau réel (tests/test_main_limits.py, 5 tests, dont la non-régression du spool_max_size).Suivi : STATUS.md (T-017 done, note détaillée incluant la découverte spool_max_size), CHANGELOG.md (Unreleased, Added ×3 + Fixed ×1). Pas de nouvel écart ouvert dans ECARTS.md — le problème de spoolage disque découvert pendant cette tâche est corrigé dans la même PR, pas laissé en dette. Pas de nouvelle ADR nécessaire (détail d'implémentation, ne contredit aucune décision existante).
Latence : middleware = un lookup de path + parse d'un entier, coût négligeable sur le chemin hotkey-up → paste ; le monkeypatch spool_max_size s'exécute une seule fois à l'import.
Aucun point bloquant. Prêt à merger.