Comble le gap : les voix fraîches + leur manifeste (scope/owner/reference_text) vivent
dans Android/data/com.kazeia.admin/files/voix/ (VoiceStorage.kt), pas dans /voices.
- admin_ingest.py : list_manifests (ls + pull .json), delete_admin_voice. Schéma fondé
sur VoiceStorage.save().
- orchestrator : list_admin_voices, ingest_voice (pull→archive→scope/owner/consentement
→verrou si exclusive→enroll ASR→deploy selon scope), sync_admin. pending = archivée,
ni enrôlée ni déployée. exclusive owner absent = verrou_profil_absent.
- _patient_omnivoice_dir robuste : env → wav_path legacy → sonde MODELS_DIR device
(post-migration /voices.wav_path est VIDE pour une voix déployée).
- store : colonnes scope/owner_name/consent_text_enc (consentement chiffré, PII) +
migration + set_voice_manifest. consent_text déchiffré dans voice_record/voices.
- autosync → sync_admin (le déclencheur auto ingère depuis l'admin).
- API : GET /voices/{serial}/admin, POST /voices/{serial}/admin/sync.
- enroll = ASR du 16s (migration §6) ; reference_text gardé comme trace de consentement.
- tests +8. 46/46. Validé live : manifeste synthétique → .ovsp déployé sur tablette.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- store : colonne locked_profile_id (+ migration ALTER idempotente), lock_voice/
unlock_voice. Verrou = assignation exclusive à un profil ; NULL = voix généraliste.
- orchestrator : list_voices expose locked_profile_id + used_by_profiles (croise
profile.voice_id) ; sync_device enrôle+déploie les voix en attente, REFUSE de
déployer une voix verrouillée sur une tablette sans le profil propriétaire.
- autosync : VoiceAutoSync — watcher de connexions armable depuis l'UI (désarmé par
défaut), statut par tablette, ne fait rien si store verrouillé (PII), delete opt-in.
- API : lock/unlock, POST /{serial}/sync, GET/POST /voices/autosync (déclarées AVANT
/{serial} pour éviter la capture "autosync" comme serial).
- tests : +8 (verrou, sync verrou-conscient, autosync). 39/39.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- API : /api/lock-status, /api/unlock, /api/lock, PUT /api/devices/{serial}/label,
/api/audit ; /api/devices enrichi du label patient quand le store est déverrouillé.
- db.py thread-safe (check_same_thread=False + RLock) pour le threadpool FastAPI ;
ajout lock(), device_labels().
- UI : barre de déverrouillage opérateur, affichage 👤 label, édition ✎ du nom.
- Sécurité : labels chiffrés (jamais en clair en base ni dans l'audit), opérations
journalisées, store re-verrouillable.
- tests : +5 intégration API↔store (dont non-fuite PII dans l'audit). 19/19.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>