mirror of
https://github.com/siop-spelev/siop2.git
synced 2026-08-08 12:41:54 +00:00
fix(mobile): dictée validée sur iPhone physique — 2 bugs réels corrigés
setAudioModeAsync({allowsRecording:true}) manquant avant recorder.record()
(RecordingDisabledException iOS) ; EXPO_USE_PRECOMPILED_MODULES/
RCT_USE_PREBUILT_RNCORE jamais persistés dans .env (documentés dans ADR-005
mais seulement exportés en shell ad-hoc) — corrigé pour que tout rebuild
depuis zéro ne reproduise plus le crash dyld. Recette terrain complète
vérifiée : dictée → note → clôture → réindexation → recherche sémantique.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -68,5 +68,6 @@ pnpm + Turborepo. `apps/api` : NestJS, Prisma, PostgreSQL (pgvector + PostGIS),
|
||||
- 🏁 **R5 CLOSE (17/07/2026, tag `release/r5`)** : recettée par le référent (revue pixel + arbitrage tableau), recette passée SANS clé API, durcissement répété en local conteneurisé. **R0 → R5 : périmètre v1 couvert.**
|
||||
- ✅ **Recette terrain mobile sur iPhone 15 Pro — 7/7 validés (19/07, ADR-005)** : Expo Go bloqué par le retard d'approbation Apple (SDK 54 vs notre SDK 57) → **décision : builds natifs locaux (Xcode/Android Studio, signature gratuite) pour la vraie recette, Expo Go conservé pour l'aperçu sans installation**. 5 obstacles techniques réels corrigés et documentés (UDID plutôt que nom, ne jamais contourner `expo run:*` par un xcodebuild manuel, modules Expo/RN précompilés SDK 56/57 incompatibles avec la liaison statique du projet, `fetch` global incompatible avec l'upload multipart natif — fix permanent `EXPO_PUBLIC_USE_RN_FETCH=1` dans `apps/mobile/.env`, découverte réseau du dev client peu fiable). Recette iOS : connexion, scan caméra réel (résolution + rejet QR étranger), vrai mode avion, conflit D2 tranché par l'humain avec photo réellement téléversée, persistance à travers fermeture/reconstruction de l'app, suggestions R5 au vrai modèle, purge de sécurité au changement de compte — tout validé. Android : build natif sur émulateur, passage santé complet.
|
||||
- ✅ **Dictée implémentée (22/07, ADR-004 §5)** : écran Voix R5 (jamais construit) — `faster-whisper` local (CTranslate2, CPU, opt-in `AI_TRANSCRIPTION=off|locale`, défaut off), audio jamais persisté (purge serveur ET mobile quoi qu'il arrive), transcription → relecture humaine → suggestion immédiate et/ou sauvegarde dans `InterventionReport.note` (champ existant depuis R2, jamais eu d'écran jusqu'ici) → `note` rejoint le corpus à la clôture comme les bilans codés. Mobile : `expo-audio`/`expo-file-system`, bouton dicter sur l'écran de clôture. Docker `siop2-ai` embarque le modèle (1,54→2,19 Go). Vérifié réellement (voix de synthèse → texte fidèle, bout en bout API, conteneur Docker construit, chaîne corpus complète note→clôture→réindexation→recherche). 80 tests API, 29 pytest ai, mobile vert.
|
||||
- 🔄 **Reprise ici** : test tactile de la dictée sur iPhone (bloqué par une connexion USB qui ne s'est pas rétablie malgré câble/port/redémarrage — reporté comme la recette Android). Redéploiement Dokploy de l'instance ENSET (`AI_SERVICE_TOKEN` à créer — runbook §2 — puis « Réindexer tout »). Restes : recette Android sur appareil physique, calibrage `AI_SEUIL_*` et qualité darija sur corpus SPELEV réel, secret `DOKPLOY_WEBHOOK_URL`, production client SPELEV (attend les accès serveur du partenaire).
|
||||
- ✅ **Dictée validée sur iPhone physique (01/08, ADR-004 §5)** : le blocage USB du 22/07 était un faux négatif de `system_profiler` (outil de diagnostic en panne, pas le câble). Deux bugs réels trouvés et corrigés : `expo-audio` iOS exige `setAudioModeAsync({ allowsRecording: true })` avant `record()` (absent, jamais détecté hors appareil réel) ; le correctif des modules précompilés (ADR-005) n'avait jamais été rendu permanent dans `apps/mobile/.env` — corrigé. Chaîne complète vérifiée en vrai : micro → dictée → transcription fidèle → note → clôture → réindexation → **retrouvé par la recherche sémantique**.
|
||||
- 🔄 **Reprise ici** : redéploiement Dokploy de l'instance ENSET (`AI_SERVICE_TOKEN` à créer — runbook §2 — puis « Réindexer tout »). Restes : recette Android sur appareil physique, calibrage `AI_SEUIL_*` et qualité darija sur corpus SPELEV réel, secret `DOKPLOY_WEBHOOK_URL`, production client SPELEV (attend les accès serveur du partenaire).
|
||||
- Détail quotidien : `docs/journal/journal.md`. Dépôt : `siop-spelev/siop2` (privé), jalons R0→R5.
|
||||
|
||||
@@ -6,3 +6,11 @@
|
||||
# boucle de retry silencieuse — trouvé en recette terrain iOS (17/07/2026).
|
||||
# Voir ADR-005 et docs/journal/journal.md.
|
||||
EXPO_PUBLIC_USE_RN_FETCH=1
|
||||
|
||||
# Modules Expo/RN précompilés (XCFrameworks, SDK 56/57) incompatibles avec la
|
||||
# liaison statique de ce projet (pas de use_frameworks!) : sans ce réglage,
|
||||
# l'app compile mais CRASHE au lancement
|
||||
# (dyld: Library not loaded: @rpath/React.framework/React, signal 6).
|
||||
# Force la recompilation depuis les sources — voir ADR-005.
|
||||
EXPO_USE_PRECOMPILED_MODULES=0
|
||||
RCT_USE_PREBUILT_RNCORE=0
|
||||
|
||||
@@ -2,7 +2,12 @@ import * as FileSystem from 'expo-file-system';
|
||||
import { router, useLocalSearchParams } from 'expo-router';
|
||||
import { useState } from 'react';
|
||||
import { Pressable, ScrollView, Text, TextInput, View } from 'react-native';
|
||||
import { RecordingPresets, requestRecordingPermissionsAsync, useAudioRecorder } from 'expo-audio';
|
||||
import {
|
||||
RecordingPresets,
|
||||
requestRecordingPermissionsAsync,
|
||||
setAudioModeAsync,
|
||||
useAudioRecorder,
|
||||
} from 'expo-audio';
|
||||
import { SafeAreaView } from 'react-native-safe-area-context';
|
||||
import {
|
||||
BILAN_FIELD_LABELS,
|
||||
@@ -197,6 +202,9 @@ function CarteSuggestion({
|
||||
const demarrerDictee = async () => {
|
||||
const { granted } = await requestRecordingPermissionsAsync();
|
||||
if (!granted) return;
|
||||
// iOS refuse recorder.record() tant que la session audio n'a pas été
|
||||
// explicitement autorisée à enregistrer (RecordingDisabledException).
|
||||
await setAudioModeAsync({ allowsRecording: true, playsInSilentMode: true });
|
||||
await recorder.prepareToRecordAsync();
|
||||
recorder.record();
|
||||
setEnregistrement(true);
|
||||
@@ -205,6 +213,7 @@ function CarteSuggestion({
|
||||
|
||||
const terminerDictee = async () => {
|
||||
await recorder.stop();
|
||||
await setAudioModeAsync({ allowsRecording: false }); // n'arme le micro que le temps de dicter
|
||||
setEnregistrement(false);
|
||||
const uri = recorder.uri;
|
||||
if (!uri) return;
|
||||
|
||||
@@ -80,6 +80,18 @@
|
||||
- Implémentée et vérifiée le 22/07/2026 : transcription réelle (français, voix de
|
||||
synthèse) juste et rapide en local, bout en bout via l'API NestJS, et dans l'image
|
||||
Docker de production construite pour l'occasion (modèle embarqué, aucun téléchargement
|
||||
au démarrage). **Reste** : le test tactile sur iPhone physique (bouton dicter, permission
|
||||
micro) n'a pas pu se jouer — blocage USB persistant malgré câble/port/redémarrage
|
||||
multiples, reporté comme la recette Android (attend un accès matériel qui fonctionne).
|
||||
au démarrage). Le test tactile sur iPhone physique n'avait pas pu se jouer ce jour-là
|
||||
(blocage USB), reporté.
|
||||
- **Recette terrain sur iPhone physique complétée le 01/08/2026** : micro, dictée réelle,
|
||||
transcription fidèle, sauvegarde dans le bilan, clôture, puis retrouvée par la recherche
|
||||
sémantique après réindexation — la chaîne complète (D5) est validée sur vrai matériel.
|
||||
Deux bugs réels trouvés et corrigés en route :
|
||||
1. `expo-audio` sur iOS refuse `recorder.record()` tant que la session audio n'a pas
|
||||
été explicitement autorisée à enregistrer — `RecordingDisabledException`, corrigée
|
||||
par un appel à `setAudioModeAsync({ allowsRecording: true })` avant l'enregistrement
|
||||
(et remis à `false` après, par hygiène — le micro ne reste pas « armé »).
|
||||
2. Le contournement des modules Expo/RN précompilés (`EXPO_USE_PRECOMPILED_MODULES=0`,
|
||||
`RCT_USE_PREBUILT_RNCORE=0` — ADR-005) n'avait **jamais été rendu permanent** dans
|
||||
`apps/mobile/.env`, contrairement au correctif FormData voisin : une reconstruction
|
||||
ultérieure sans ces variables aurait reproduit le crash au lancement déjà documenté.
|
||||
Désormais committé aux côtés de `EXPO_PUBLIC_USE_RN_FETCH`.
|
||||
|
||||
@@ -4,6 +4,39 @@ Trace chronologique des sessions (la plus récente en premier). Le **playbook**
|
||||
|
||||
---
|
||||
|
||||
## 2026-08-01 — Pr. Daaif (+ Claude) — Dictée validée sur iPhone physique (ADR-004 §5)
|
||||
|
||||
**Actions**
|
||||
|
||||
- Reprise du reste laissé le 22/07 : le test tactile de la dictée sur iPhone, alors bloqué par une connexion
|
||||
USB qui ne se rétablissait pas. Diagnostic cette fois : `system_profiler` (l'outil utilisé pour vérifier la
|
||||
détection USB) était lui-même en panne et renvoyait un faux négatif silencieux — `ioreg` a révélé que
|
||||
l'iPhone était en réalité détecté. Le vrai câble/port n'a jamais été le problème.
|
||||
- Une fois l'app reconstruite (certificat gratuit expiré après plus d'une semaine — régénéré via le pipeline
|
||||
officiel `expo run:ios`, jamais par un contournement manuel), **deux bugs réels trouvés et corrigés** :
|
||||
1. `expo-audio` sur iOS refuse d'enregistrer sans un appel explicite à `setAudioModeAsync({ allowsRecording:
|
||||
true })` avant `recorder.record()` — absent du code initial, jamais détecté hors d'un vrai appareil.
|
||||
2. Le correctif des modules Expo/RN précompilés (ADR-005, `EXPO_USE_PRECOMPILED_MODULES=0` /
|
||||
`RCT_USE_PREBUILT_RNCORE=0`) n'avait **jamais été committé** dans `apps/mobile/.env`, contrairement au
|
||||
correctif FormData voisin déjà permanent — un vrai trou qui aurait refait planter l'app à la prochaine
|
||||
reconstruction depuis zéro. Corrigé : les deux variables vivent maintenant côte à côte dans `.env`.
|
||||
- **Recette réelle bout en bout** : micro → dictée d'une phrase → transcription fidèle (`faster-whisper` réel)
|
||||
→ relecture → sauvegarde dans le bilan → clôture de l'OT → réindexation du corpus → **le contenu dicté
|
||||
retrouvé par la recherche sémantique** (score 0,476). La boucle complète de l'idée d'origine (« cette
|
||||
transcription devrait alimenter le corpus ») est validée sur vrai matériel, vraie voix, vrai réseau.
|
||||
- Infra locale relancée depuis zéro après une semaine d'inactivité (Docker, conteneurs `siop2-*`, API,
|
||||
service IA) — occasion de vérifier que le redémarrage à froid fonctionne proprement.
|
||||
|
||||
**Décisions**
|
||||
|
||||
- Aucune — cette session referme un reste déjà décidé, sans nouvelle décision de conception.
|
||||
|
||||
**Prochaine étape** : recette Android sur appareil physique (toujours en attente d'un appareil) ; redéploiement
|
||||
Dokploy (`AI_SERVICE_TOKEN`) ; calibrage `AI_SEUIL_*` et qualité darija sur corpus SPELEV réel ; secret
|
||||
`DOKPLOY_WEBHOOK_URL`.
|
||||
|
||||
---
|
||||
|
||||
## 2026-07-22 — Pr. Daaif (+ Claude) — Dictée implémentée : audio → transcription locale → corpus (ADR-004 §5)
|
||||
|
||||
**Actions**
|
||||
|
||||
Reference in New Issue
Block a user