From 0924174c7b7284493dde00dbb22eef2b1355df6b Mon Sep 17 00:00:00 2001 From: alf Date: Fri, 19 Jun 2026 20:21:34 +0200 Subject: [PATCH] =?UTF-8?q?docs:=20VOICE=5FDEPLOYMENT=5FSPEC=20=C2=A74=20r?= =?UTF-8?q?equis=20=E2=80=94=20multi-patients/tablette=20=E2=86=92=20exclu?= =?UTF-8?q?sivit=C3=A9=20on-device?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Décidé : une tablette héberge plusieurs profils patients. La localisation du .cvps ne suffit pas → mécanisme sidecar cosyvoice/.owner (profileId, absent=généraliste) poussé par Kazeia-central ; app lit le sidecar, expose owner_profile_id à /voices, filtre le picker, repli runtime sur DEFAULT_VOICE_ID si profil non-propriétaire. Co-Authored-By: Claude Opus 4.8 (1M context) --- docs/VOICE_DEPLOYMENT_SPEC.md | 62 +++++++++++++++++++---------------- 1 file changed, 34 insertions(+), 28 deletions(-) diff --git a/docs/VOICE_DEPLOYMENT_SPEC.md b/docs/VOICE_DEPLOYMENT_SPEC.md index c388b16..4d0bb84 100644 --- a/docs/VOICE_DEPLOYMENT_SPEC.md +++ b/docs/VOICE_DEPLOYMENT_SPEC.md @@ -70,43 +70,49 @@ propre, sans aller-retour WebDAV ni redémarrage. --- -## 3. Politique de déploiement (RAPPEL — appliquée côté Kazeia-central) +## 3. Politique de déploiement (appliquée côté Kazeia-central) -Pour mémoire, **pas une demande de code app** : la règle est **enforced par -Kazeia-central au moment du déploiement** — +La règle de **localisation** est enforced par Kazeia-central au déploiement — - une voix **verrouillée** (exclusive à un profil patient) n'est poussée **que** sur la/les tablette(s) où ce profil est déployé ; -- seules les voix **globales/non-exclusives** sont déployées sur les tablettes (parc). +- seules les voix **globales/non-exclusives** sont déployées sur le parc. -L'app n'a donc **rien à faire** pour cette règle dans le cas **1 patient ↔ 1 tablette** -(modèle de prod implicite, cf. `CLAUDE.md §9` « serial → label patient ») : l'exclusivité -= la localisation du `.cvps`, décidée par le PC. +⚠️ Mais une tablette héberge **plusieurs profils patients** (confirmé). La localisation +ne suffit donc **pas** : un `.cvps` exclusif à A, présent sur la tablette partagée +(parce que A y est), serait techniquement sélectionnable par B. → l'exclusivité doit +aussi être enforced **on-device** : c'est l'objet du §4, qui devient **requis**. --- -## 4. Adaptation **CONDITIONNELLE** — exclusivité on-device (tablettes multi-patients) +## 4. Adaptation **REQUISE** — exclusivité on-device (tablettes multi-patients) -⚠️ **À trancher (Richard / dev)** : si une tablette héberge **plusieurs profils -patients** (observé sur la tablette de dev : `p_test01`, `p_test02`), alors un `.cvps` -exclusif à A, présent sur la tablette parce que A y est, est **techniquement chargeable -par le profil B** (résolution par nom de fichier, aucun contrôle de propriété). La règle -« déploiement uniquement où le profil est » ne suffit alors plus à garantir l'exclusivité -*à l'intérieur* d'une tablette partagée. +Décidé : une tablette héberge **plusieurs profils patients**. Une voix exclusive ne doit +donc être **ni sélectionnable, ni audible** par un profil qui n'en est pas propriétaire, +même si son `.cvps` est physiquement sur la tablette. -Deux options : -- **(a) Mono-patient par tablette (recommandé, défaut supposé)** : aucune adaptation. - L'exclusivité = la localisation du `.cvps`. C'est le modèle « une tablette = un - patient » du parc. -- **(b) Multi-patients par tablette** : l'app doit connaître le **propriétaire** d'une - voix exclusive et empêcher un autre profil de la sélectionner. Nécessite : - - un marqueur de propriété par voix (ex. fichier `cosyvoice/.owner` = profileId, - poussé par Kazeia-central, ou colonne `owner_profile_id` à `/voices`) ; - - côté picker/`setVoiceId` : masquer/refuser une voix exclusive pour un profil ≠ - propriétaire (repli sur la voix par défaut). +### 4.1 Marqueur de propriété — sidecar `.owner` +Kazeia-central pousse, **à côté** du `.cvps`, un fichier sidecar : +``` +cosyvoice/.cvps # l'artefact voix (inchangé) +cosyvoice/.owner # présent SI exclusive → contient le profileId propriétaire +``` +- **Absence de `.owner`** ⇒ voix **généraliste** : sélectionnable par tout profil. +- **Présence de `.owner`** ⇒ voix **exclusive** au `profileId` qu'il contient (UTF-8, + une ligne). Cohérent avec le modèle « tout par fichier + `adb push` » (comme le `.cvps`). -> Décision attendue : **(a)** suffit si le parc est mono-patient/tablette. Sinon **(b)** -> et on étend cette spec. Kazeia-central est prêt pour les deux (il connaît déjà -> `locked_profile_id` par voix). +### 4.2 Enforcement côté app +- **`/voices`** : exposer une colonne **`owner_profile_id`** (lue depuis `.owner`, + vide si généraliste) — pour que le picker filtre. +- **Picker / assignation `Profile.voiceId`** : ne pas proposer/permettre l'assignation + d'une voix exclusive à un profil ≠ propriétaire. +- **Runtime (`KazeiaService.setVoiceId` / `CosyVoiceTtsEngine.ensureVoice`)** : garde-fou + défensif — si le profil actif n'est pas propriétaire d'une voix exclusive demandée, + **repli sur `DEFAULT_VOICE_ID`** (ne jamais faire parler un patient avec la voix + exclusive d'un autre). + +> Kazeia-central connaît déjà `locked_profile_id` par voix et poussera/retirera le +> sidecar `.owner` au verrouillage/déverrouillage. L'app n'a qu'à **lire** le sidecar et +> appliquer le filtrage + le garde-fou runtime. --- @@ -116,7 +122,7 @@ Deux options : | --- | --- | --- | --- | | 1 | `/voices` scanne WAV (« à convertir ») **et** `.cvps` (« prête »), col. `cvps_exists` | inventaire voulu | curseur `voicesCursor()` | | 2 | `voices_reload` (rescan + `RELOAD_VOICES`, constante `ACTION_RELOAD_VOICES`) | important | méthode `call()` | -| 4 | exclusivité on-device | conditionnel (si multi-patients/tablette) | à décider §4 | +| 4 | exclusivité on-device : lire `cosyvoice/.owner`, col. `owner_profile_id`, filtrer picker + repli runtime | **requis** (multi-patients) | `voicesCursor()` + picker + `setVoiceId` | Côté Kazeia-central (déjà fait / à faire, **pas** côté app) : enrôlement, archive chiffrée du WAV, suppression du WAV device, **politique de déploiement verrou-conscient**,