Compare commits
2 Commits
c92c28098e
...
c0308a95c0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c0308a95c0 | ||
|
|
a2d4eb2944 |
50
kanban.html
50
kanban.html
@ -456,11 +456,11 @@
|
||||
<section class="summary" aria-label="Chiffres clés">
|
||||
<div class="stat"><span class="n n-critical">1</span><span class="k">Urgent</span></div>
|
||||
<div class="stat"><span class="n n-info">1</span><span class="k">Migration v3</span></div>
|
||||
<div class="stat"><span class="n n-warn">2</span><span class="k">Bugs ouverts</span></div>
|
||||
<div class="stat"><span class="n n-warn">1</span><span class="k">Bugs ouverts</span></div>
|
||||
<div class="stat"><span class="n">6</span><span class="k">À tester</span></div>
|
||||
<div class="stat"><span class="n">24</span><span class="k">Planifié</span></div>
|
||||
<div class="stat"><span class="n">23</span><span class="k">Planifié</span></div>
|
||||
<div class="stat"><span class="n n-gate">8</span><span class="k">Bascule prod</span></div>
|
||||
<div class="stat"><span class="n n-good">54</span><span class="k">Fait récemment</span></div>
|
||||
<div class="stat"><span class="n n-good">56</span><span class="k">Fait récemment</span></div>
|
||||
</section>
|
||||
|
||||
<div class="filters" role="group" aria-label="Filtrer par domaine">
|
||||
@ -517,21 +517,10 @@
|
||||
|
||||
<!-- BUGS -->
|
||||
<section class="col" style="--stripe: var(--warn)">
|
||||
<div class="col-head"><h2>Bugs ouverts</h2><span class="count">2</span></div>
|
||||
<div class="col-head"><h2>Bugs ouverts</h2><span class="count">1</span></div>
|
||||
<div class="stack">
|
||||
|
||||
<article class="card" data-area="backend visitapp" data-horizon="v1">
|
||||
<div class="card-meta"><span class="tag">backend</span><span class="tag">visitapp</span><span class="flag f-critical">Bloque l'activation — 13/08</span></div>
|
||||
<h3>Les déclenchements proactifs pollueraient « Ce que demandent vos visiteurs »</h3>
|
||||
<p>✅ <strong>Le proactif est câblé le 13/08</strong> (garde, cycle de vie, points GPS depuis <code>meterZoneGPS</code>, réglage visiteur) et <strong>M3 est fermé avec lui</strong> — APK <code>dev</code> vert. <strong>Il reste ce point, et il est bloquant avant d'activer le mode chez qui que ce soit.</strong></p>
|
||||
<p>⚠️ <strong><code>RecordVisitorQuestion</code> écrit <code>Question = request.Message</code></strong> (<code>AiController:135</code>). Or le prompt d'un déclenchement automatique est une consigne machine : « Tu es un guide audio de musée. Le visiteur vient d'entrer dans la zone X. Accueille-le… ». Chaque passage devant une œuvre <strong>ajouterait donc une fausse « question de visiteur »</strong> dans l'onglet Guide IA — un écran qui existe précisément pour montrer ce que les humains demandent.</p>
|
||||
<p><strong>Et ça ne s'arrête pas à du bruit</strong> : <code>HasAnswer</code> se déduit des sources, donc un prompt d'accueil sans citation viendrait aussi <strong>gonfler le bloc ambre « questions sans réponse »</strong>, celui qui sert à repérer les trous de contenu. Même famille que <code>WeatherSyncService</code> noyant le journal d'audit : le flot machine noie le signal humain dans l'écran fait pour lui.</p>
|
||||
<p><strong>À faire</strong> : un drapeau porté par <code>AiChatRequest</code> (déclenchement automatique vs question posée), et <code>RecordVisitorQuestion</code> qui n'enregistre pas — ou marque distinctement — les tours d'origine machine. <strong>Deux repos</strong> : <code>manager-service</code> puis <code>mymuseum-visitapp</code>. ⚠️ À trancher en même temps : ces tours consomment des jetons du quota client, donc <code>RecordUsage</code>, lui, doit continuer à les compter.</p>
|
||||
<p>✅ <strong>Écouteurs : ne pas les détecter</strong> — confirmé le 13/08. Le mode est opt-in via un switch dans <code>VoiceModeSheet</code> ; le sous-titre dit « écouteurs recommandés ». On informe, on n'arbitre pas à la place du visiteur.</p>
|
||||
<span class="src">v1-plan.md lot F — proactif & M3</span>
|
||||
</article>
|
||||
|
||||
<article class="card" data-area="visitapp">
|
||||
<article class="card" data-area="visitapp">
|
||||
<div class="card-meta"><span class="tag">visitapp</span></div>
|
||||
<h3>Échecs de téléchargement silencieux</h3>
|
||||
<p>Un fichier raté fait un <code>print</code> et passe. La visite est annoncée téléchargée alors qu'elle est incomplète.</p>
|
||||
@ -595,7 +584,7 @@
|
||||
|
||||
<!-- PLANIFIÉ -->
|
||||
<section class="col" style="--stripe: var(--ink-3)">
|
||||
<div class="col-head"><h2>Planifié</h2><span class="count">24</span></div>
|
||||
<div class="col-head"><h2>Planifié</h2><span class="count">23</span></div>
|
||||
|
||||
|
||||
<div class="stack">
|
||||
@ -667,16 +656,6 @@
|
||||
<span class="src">v1-plan.md — lot J · cgu-myinfomate.md §8 · mention-information-visiteurs.md</span>
|
||||
</article>
|
||||
|
||||
<article class="card" data-area="visitapp" data-horizon="v1">
|
||||
<div class="card-meta"><span class="tag">visitapp</span><span class="tag">lunettes</span><span class="flag f-warn">Nouveau 12/08</span></div>
|
||||
<h3>Miroir de la conversation vocale dans le chat</h3>
|
||||
<p><strong>Tranché le 12/08</strong> : ouvrir l'assistant pendant que les lunettes tournent doit montrer <strong>la même conversation</strong>, transcrite au fil de l'eau. Une conversation, deux surfaces.</p>
|
||||
<p>⚠️ <strong>Le coût n'est pas l'affichage, c'est l'unification des historiques.</strong> <code>MyInfoMateLlmClient</code> construit <strong>son propre</strong> <code>AssistantService</code> (<code>maxHistory: 6</code>), distinct de celui du chat écrit : poser une question aux lunettes puis ouvrir le chat donne aujourd'hui un interlocuteur qui ne sait rien de ce qu'on vient de demander. La transcription, elle, est déjà là — <code>VoiceOrchestrator</code> expose <code>lastTranscription</code> et <code>lastTtsText</code>, personne ne les affiche.</p>
|
||||
<p>✅ <strong>Bénéfice de bord</strong> : <code>VisitorQuestion.ConversationId</code> est un GUID de session, « seul lien entre deux tours ». Deux historiques = deux conversations en base pour un visiteur qui a juste changé de surface. <strong>Unifier répare aussi les agrégats du Guide IA</strong>, donc ce que le job de thèmes du lot J regroupera. ⛔ Écarté : l'affichage passif dans <code>VoiceModeSheet</code> (2-3 h) — ça montrait que ça marche sans réparer la coupure. ~1 à 2 jours.</p>
|
||||
<p>⚠️ <strong>Ne rien redessiner, l'UI existe.</strong> <code>GlassesStatusWidget</code> est monté en haut à droite de l'accueil (<code>home_3.0.dart:465</code>) : état par couleur, pastille de mode actif, tap → <code>VoiceModeSheet</code> (connexion, choix de mode, désactivation). <code>GlassesDebugPanel</code> existe et <strong>se garde</strong> — c'est l'outil de test. Seul écart avec « tout le temps présente », et il est volontaire : l'icône se cache si <code>applicationInstanceDTO?.isAssistant != true</code> (<code>GlassesStatusWidget.dart:32</code>) — invisible chez MDLF et le Fort, visible sur l'instance MyInfoMate. <strong>À savoir avant de tester chez un client et de croire à un bug.</strong></p>
|
||||
<span class="src">v1-plan.md lot F — miroir vocal</span>
|
||||
</article>
|
||||
|
||||
<article class="card" data-area="manager" data-horizon="v1">
|
||||
<div class="card-meta"><span class="tag">manager-app</span><span class="flag f-warn">Jamais ouvert</span></div>
|
||||
<h3>Écrans du lot design — vérification à l'œil</h3>
|
||||
@ -875,9 +854,24 @@
|
||||
|
||||
<section class="done">
|
||||
<h2>Fait récemment</h2>
|
||||
<p>Cinquante-quatre chantiers clos entre le 5 et le 13 août 2026.</p>
|
||||
<p>Cinquante-six chantiers clos entre le 5 et le 13 août 2026.</p>
|
||||
<div class="done-grid">
|
||||
|
||||
<div class="done-item">
|
||||
<strong>Une conversation, plusieurs surfaces — miroir vocal et <code>conversationId</code></strong>
|
||||
<span>Le chat écrit, le vocal et le proactif partagent désormais <strong>une seule</strong> conversation, portée par <code>VisitAppContext.assistant</code>. Les trois <code>AssistantService</code> séparés ont disparu ; le <code>maxHistory: 6</code> du vocal s'aligne sur 10, deux surfaces qui partagent une conversation ne pouvant pas la tronquer différemment selon le point d'entrée. La concision vocale vient d'<code>isVoice</code>, qui change le prompt côté serveur.</span>
|
||||
<span>⚠️ <strong>Le vrai obstacle n'était pas l'affichage mais la forme du chat.</strong> <code>AssistantChatSheet</code> gardait ses messages en <code>List<Widget></code> — des bulles déjà construites. On ne rejoue pas une conversation à partir de widgets, et un tour vocal survenu pendant que la feuille était fermée n'aurait jamais pu y entrer. Le service porte maintenant les tours <strong>en données</strong> et notifie ; la feuille rend depuis eux et se met à jour en direct.</span>
|
||||
<span>⚠️ <strong>Deux listes, délibérément</strong> : celle envoyée au modèle et celle affichée ne coïncident pas. Un message d'erreur se montre sans repartir au guide — il n'a pas à s'excuser d'une panne au tour suivant ; le prompt d'un déclenchement proactif part au guide <strong>sans jamais s'afficher</strong>, seule sa réponse apparaît, marquée « À voix haute ». Sans ce marquage, le visiteur trouverait dans son chat des messages qu'il n'a jamais tapés.</span>
|
||||
<span>✅ <strong><code>conversationId</code> est enfin envoyé — et le champ manquait dans le client généré</strong>, ce qui explique que personne ne l'envoyait. Ajouté à la main. Il est renouvelé quand la conversation est vidée, sinon la visite entière d'un visiteur n'en formerait qu'une. 2 tests serveur fixent le lien entre deux tours et le repli <code>Guid.NewGuid()</code>, que rien ne couvrait. <code>dotnet test</code> 215, APK <code>dev</code> ✅, <code>flutter build web</code> ✅.</span>
|
||||
</div>
|
||||
|
||||
<div class="done-item">
|
||||
<strong>Les prompts du mode proactif ne polluent plus « Ce que demandent vos visiteurs »</strong>
|
||||
<span>Le dernier bloquant du proactif. <code>RecordVisitorQuestion</code> écrivait <code>Question = request.Message</code> : chaque passage devant une œuvre aurait ajouté <strong>une fausse question de visiteur</strong> — une consigne que le système s'écrit à lui-même — dans l'écran fait pour montrer ce que les <strong>humains</strong> demandent. Et <code>HasAnswer</code> se déduisant des sources, un prompt d'accueil sans citation aurait <strong>gonflé le bloc ambre des questions sans réponse</strong>. Même famille que <code>WeatherSyncService</code> noyant le journal d'audit.</span>
|
||||
<span><strong>Tranché : ne rien journaliser, plutôt que marquer d'un drapeau.</strong> Une ligne <code>VisitorQuestion</code> sert au rapport de trous de contenu et aux thèmes du lot J ; un prompt machine n'alimente ni l'un ni l'autre, et le garder « marqué » obligerait <strong>chaque futur agrégat</strong> à penser à l'exclure — la classe de panne qu'on venait de fermer sur <code>AuditedTypes</code>. ⚠️ <strong>Mais les jetons restent comptés</strong> : ces tours coûtent de l'argent réel au quota, et un audioguide proactif peut en consommer beaucoup. 2 tests fixent la paire — pas de ligne, compteur incrémenté — parce que c'est la combinaison qui compte.</span>
|
||||
<span>Trois repos : <code>manager-service</code> (<code>dotnet test</code> 213), <code>manager-app</code> (<code>manager_api_new</code> étendu à la main, <code>flutter build web</code> ✅ — le client est partagé, donc vérifié) et <code>mymuseum-visitapp</code> (APK <code>dev</code> ✅).</span>
|
||||
</div>
|
||||
|
||||
<div class="done-item">
|
||||
<strong>Déclenchement proactif câblé, et M3 fermé avec lui</strong>
|
||||
<span>Quatre branchements : la garde, le cycle de vie, les points GPS, le réglage visiteur. APK <code>dev</code> vert. ⚠️ <strong>La garde recommandée par le plan aurait coûté de l'argent</strong> : <code>_trigger</code> appelle le LLM d'abord et ne parle qu'ensuite via <code>activeVoiceOrchestrator?.ttsEngine</code>, dont le <code>?.</code> avale le cas « aucun mode vocal actif ». Avec <code>proactiveModeEnabled</code> seul, un visiteur traversant une zone avec l'app en poche et le vocal éteint consommait des jetons Gemini facturés au client pour une phrase que personne n'entend. La garde retenue interroge l'orchestrateur : la vraie condition n'est pas « quel matériel » mais « y a-t-il quelqu'un pour écouter ».</span>
|
||||
|
||||
18
v1-plan.md
18
v1-plan.md
@ -176,6 +176,16 @@ Ne change pas : la popup de traduction (un niveau justifié, langues verticales,
|
||||
|
||||
✅ **Volet `manager-service` terminé le 2026-08-13.** Customer Portal livré, ratio jetons → questions calé sur du réel ; le rate limiting, l'endpoint `ApplicationInstance` et les quotas seed **étaient déjà faits** et n'attendaient qu'une vérification (détail dans les lignes concernées). `dotnet build` vert, `dotnet test` **211 au total : 197 passés, 14 sautés** (Testcontainers, pas de démon Docker), 0 échec.
|
||||
|
||||
✅ **Miroir de la conversation vocale livré le 2026-08-13.** `flutter analyze lib` sans erreur, APK `dev` ✅, `dotnet test` **215**, `flutter build web` ✅.
|
||||
|
||||
**Une seule conversation, portée par `VisitAppContext.assistant`.** Le chat écrit, le vocal et le proactif partagent l'instance ; les trois `AssistantService` séparés ont disparu. Le `maxHistory: 6` du vocal s'aligne sur 10 : deux surfaces qui partagent une conversation ne peuvent pas la tronquer différemment selon le point d'entrée, et la concision vocale vient d'`isVoice`, qui change le prompt côté serveur.
|
||||
|
||||
⚠️ **Le vrai obstacle n'était pas l'affichage mais la forme du chat** : `AssistantChatSheet` gardait ses messages en `List<Widget>` — des bulles déjà construites. On ne rejoue pas une conversation à partir de widgets, et un tour vocal survenu pendant que la feuille était fermée n'aurait jamais pu y entrer. Le service porte désormais les tours **en données** (`AssistantTurn`) et notifie ; la feuille rend depuis eux et se met à jour en direct.
|
||||
|
||||
⚠️ **Deux listes, et c'est délibéré** : `_history` (envoyée au modèle) et `_turns` (affichée) ne coïncident pas. Un message d'erreur se montre sans repartir au guide — il n'a pas à s'excuser d'une panne au tour suivant ; et le prompt d'un déclenchement proactif part au guide **sans jamais s'afficher**, seule sa réponse apparaît, marquée « À voix haute ». Sans ce marquage, un visiteur ouvrant le chat trouverait des messages qu'il n'a jamais tapés.
|
||||
|
||||
✅ **`conversationId` est enfin envoyé, et le champ manquait dans le client généré** — c'est ce qui expliquait que personne ne l'envoyait. Ajouté à la main dans `manager_api_new`. Il est renouvelé quand la conversation est vidée : sinon la visite entière d'un même visiteur n'en formerait qu'une. **2 tests serveur** fixent le lien entre deux tours et le repli — la branche `Guid.NewGuid()` n'était couverte par rien.
|
||||
|
||||
**Ce qui reste au lot F, et dans quel repo** — le backend ne bloque plus rien :
|
||||
- ~~`manager-app`~~ ✅ **livré le 2026-08-13** : bouton du Customer Portal et diviseur `aiTokensPerQuestion`. `flutter analyze lib` **sans erreur**, `flutter build web` ✅. Détail ci-dessous.
|
||||
- `mymuseum-visitapp` : déclenchement proactif (+ M3), miroir de la conversation vocale, émission des événements vocaux. C'est désormais **tout le reste du lot F**, et le plus gros.
|
||||
@ -191,7 +201,13 @@ Ne change pas : la popup de traduction (un niveau justifié, langues verticales,
|
||||
|
||||
⚠️ **Deux pièges de format sur les points GPS, vérifiés plutôt que supposés.** `currentSections` porte des **maps JSON brutes**, pas des DTO (même idiome que `assistantSuggestions` et `ScannerDialog`), et `latitude`/`longitude` y sont des **chaînes** — c'est le type du `SectionDTO` généré. D'où `double.tryParse`. `meterZoneGPS` devient le rayon par section, la constante ne servant plus que de défaut : **c'est là que M3 se ferme**.
|
||||
|
||||
⛔ **Un point n'est PAS livré, et il bloque l'activation du mode chez qui que ce soit.** `RecordVisitorQuestion` écrit `Question = request.Message` (`AiController:135`) — or le prompt d'un déclenchement automatique est une consigne machine (« Tu es un guide audio de musée. Le visiteur vient d'entrer dans la zone X. Accueille-le… »). Chaque passage devant une œuvre ajouterait donc **une fausse question de visiteur** dans l'onglet Guide IA, écran qui existe précisément pour montrer ce que les humains demandent. Et `HasAnswer` se déduisant des sources, un prompt d'accueil sans citation **gonflerait aussi le bloc ambre « questions sans réponse »**. C'est la famille de `WeatherSyncService` noyant le journal d'audit : le flot machine noie le signal humain dans l'écran fait pour lui. **À faire dans `manager-service` puis `mymuseum-visitapp`** : un drapeau sur `AiChatRequest`, et une journalisation qui distingue les tours machine — sans cesser de compter leurs jetons au quota, eux sont bien consommés. Le code proactif est en place et compile, mais **ne pas allumer le réglage avant ça**.
|
||||
✅ **Le bloquant est levé le 2026-08-13 — `AiChatRequest.IsAutoTriggered`.** `RecordVisitorQuestion` écrivait `Question = request.Message` (`AiController:135`), or le prompt d'un déclenchement automatique est une consigne machine (« Tu es un guide audio de musée. Le visiteur vient d'entrer dans la zone X. Accueille-le… »). Chaque passage devant une œuvre aurait ajouté **une fausse question de visiteur** dans l'onglet Guide IA, écran qui existe précisément pour montrer ce que les humains demandent — et, `HasAnswer` se déduisant des sources, aurait **gonflé le bloc ambre « questions sans réponse »**. Même famille que `WeatherSyncService` noyant le journal d'audit.
|
||||
|
||||
> **Tranché : ne pas journaliser du tout, plutôt que marquer d'un drapeau.** Une ligne `VisitorQuestion` sert au rapport de trous de contenu et aux thèmes du lot J ; un prompt que le système s'est écrit à lui-même n'alimente ni l'un ni l'autre. La garder « marquée » obligerait **chaque futur agrégat** à penser à l'exclure — la classe de panne exacte qu'on venait de fermer sur `AuditedTypes`.
|
||||
>
|
||||
> ⚠️ **Mais les jetons restent comptés.** `RecordUsage` est appelé dans tous les cas : ces tours coûtent de l'argent réel au quota du client, et un audioguide proactif peut en consommer beaucoup. Les masquer du compteur serait pire que le bruit qu'on retire. **2 tests** fixent la paire — pas de ligne journalisée, compteur incrémenté — parce que c'est la combinaison qui compte, pas chaque moitié isolément.
|
||||
>
|
||||
> Trois repos, trois commits : `manager-service` (DTO + contrôleur + tests, `dotnet test` **213**), `manager-app` (`manager_api_new` étendu **à la main**, `flutter build web` ✅ — le client est partagé, donc vérifié) et `mymuseum-visitapp` (`AssistantService.chat` + l'appelant proactif, APK `dev` ✅).
|
||||
|
||||
✅ **Code mort supprimé le 2026-08-13 — `flutter analyze lib` rend zéro erreur, une première pour ce repo.** Trois fichiers Dart : `wake_word_service.dart`, `glasses_qr_scanner_service.dart` et `glasses_tts_service.dart` (l'ancien TTS ElevenLabs, importé par les deux seuls autres). Ils portaient les **4 erreurs** `kElevenLabsApiKey` / `kElevenLabsVoiceId` et l'APK se construisait quand même : personne ne les importait — un îlot hors du graphe de compilation, même mécanique que les 14 fichiers orphelins de `manager_api_new`. **C'étaient les ancêtres de `Services/Glasses/`.**<br>⚠️ **Piège d'homonymie, à ne pas re-déclencher** : `android/…/WakeWordService.kt` porte le même nom et est **bien vivant** — service natif déclaré au manifeste et piloté par `MainActivity`, c'est lui qui fait tourner le wake word. Seul le Dart est parti.<br>✅ **`impl/elevenlabs_tts_engine.dart` est conservé** : c'est l'implémentation de génération courante, gardée comme option si des clients jugeaient la voix Gemini insuffisante — beaucoup plus cher, donc pas un défaut.<br>⚠️ **Corollaire qui rétrécit le chantier du miroir vocal** : j'avais compté **cinq** `AssistantService` distincts, donc cinq historiques. Deux étaient dans ce code mort. **Il y en a trois de vivants** : le chat (`AssistantChatSheet:70`), le vocal (`MyInfoMateLlmClient:18`, `maxHistory: 6`) et le proactif (`geo_beacon_trigger_service:62`). À unifier, mais trois, pas cinq.
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user