DOCS/STATUS.md
Thomas Fransolet 78517cb38f Studio : lot 8e ajouté au plan, STATUS, kanban et plan de test à jour
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011VxSQeGQYUvPmSEoGdnidA
2026-09-15 23:00:46 +02:00

298 lines
55 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# MyInfoMate — Tableau de bord
> Point d'entrée unique : "où j'en suis, qu'est-ce que je peux avancer". Chaque section renvoie vers le document détaillé pertinent — ce fichier ne duplique pas le contenu, il donne juste la vue d'ensemble et l'état.
>
> Dernière mise à jour : 2026-09-15.
---
## 1. Produit — features manquantes / en cours
→ Détail complet, granulaire, à jour : **[todo-features.md](todo-features.md)**
Résumé de la table de suivi de ce fichier :
- **Fait** : SectionEvent, Escape/Puzzle games, GuidedPath + progression carte, push, stats tracking, sync agenda/météo, traduction IA, quotas (IA/stockage/stats) par plan, audit log backend fiabilisé, refacto SectionParcours/GuidedPath/SectionGame (backend, vérifié 2026-07-15).
- **Partiel** : Beacons/geofencing (manager-app UI ⚠️), AI Assistant (manager-app ⚠️), ~~**mode offline**~~**corrigé le 2026-08-11 (D1) puis complété le 13/08 (D5)** — le pipeline de collecte n'est plus commenté, une visite embarque désormais contenus, audios et icônes, et une visite incomplète ne s'annonce plus téléchargée. **Reste à confirmer sur device** (§21 / D0), c'est le seul point encore ouvert du volet offline. Repasse UI globale, refacto SectionParcours côté manager-app/visitapp-web/mymuseum-visitapp (UI présente mais sous-comportements non revérifiés en détail — voir section "Refactoring majeur" de todo-features.md).
- **🧪 Implémenté mais jamais testé (2026-08-05)** : **onboarding self-service complet** — inscription sur myinfomate-landing (`/[lang]/signup`), création auto de l'instance + du premier user, essai 14j, Stripe (customer/Checkout/webhook/Tax), 9 emails Resend (domaine vérifié, envoi réel validé), job Hangfire du cycle d'essai, watermark « Aperçu » visitapp-web, écran Abonnement manager-app, mot de passe oublié + invitation user, plafond IA d'essai. Les 4 repos compilent, migrations appliquées en base locale, **aucun parcours exécuté** → checklist de test dans `todo-features.md` (section "🧪 Checklist de test end-to-end"). Reste à faire côté config : alias mail `onboarding@myinfomate.be`, et `whsec_` de la Stripe CLI pour tester le webhook en local.
- **🧪 Codé et committé le 2026-09-10 (rien de poussé), pas encore testé** : scan d'id brut (QR du Fort) depuis l'accueil, drapeau « afficher le scan QR » et **nom de l'application** par app (Mobile/Web), liens stores (SuperAdmin), QR du manager vers la nouvelle page **`app.myinfomate.be/download/…`**, suggestions de proximité beacon + GPS avec **notification écran verrouillé** (mobile) et suggestion GPS dans la page (web). Tests : [test-plan.md §23](test-plan.md) ; restes hors code (redirection `web.mymuseum.be`, déclarations stores) : carte kanban « QR codes, nom d'application… ».
- **À faire** : dashboard super-admin V2, facturation V2. ✅ **Le Stripe Customer Portal est complet le 2026-08-13** (backend + bouton manager-app).
- ⚠️ **Cette ligne était périmée sur six points, revérifiés dans le code le 2026-08-13.** Sont **faits** : écran audit log manager-app (front livré le 12/08), **rate limiting API Keys** (livré, pas seulement décidé — politique `ai` dans `Startup.cs:279`, `UseRateLimiter()` en `:344`, les deux endpoints décorés), gestion des users par instance (plafond serveur + compteur UI, 12/08), nettoyage `SectionEvent.ParcoursIds` (11/08), et le **backend** du Customer Portal (13/08). L'**unification `QuestionType`** n'est pas « à faire » mais **écartée** : le trio TextLibre/Digicode/ExpectedAnswer n'a jamais existé dans le code, l'enum a trois valeurs et le digicode est un comportement dérivé, pas un type — reste une propreté front, elle aussi livrée (alias nommés, 11/08).
- **📦 Reporté en V2 (décidé le 2026-08-07)** : **SectionForm**, **ressource 360°**, **AR image tracking (Mind AR)**. Les trois sont spécifiés en détail dans [todo-features.md](todo-features.md) mais sortent du périmètre V1 — aucun n'est un prérequis de la migration Postgres ni de la mise en prod. À reprendre tels quels quand la V1 sera stabilisée. **SectionForm a été analysé dans le code le 2026-09-15** : plan complet dans [v2/section-form-plan.md](v2/section-form-plan.md) (réponses **anonymes**, les **trois** fronts visiteur kiosk compris, table `FormSubmission` dédiée, réponses jamais indexées pour le RAG) — **ne pas refaire l'analyse**.
- **📦 Reporté en V2 (décidé le 2026-09-15)** : **loader animé composable par le client**. Aujourd'hui le loader est une image fixe (`Configuration.LoaderImageUrl`), et ça suffit pour la V1. La suite est **décidée, pas exécutée** : on transporte des **paramètres** (`{preset, couleurs[], logoResourceId, vitesse}`) et chaque front rend 5-6 presets nativement — **pas de format de fichier animé**. ⛔ SVG animé et Lottie ont été évalués et **écartés** (`flutter_svg` ignore SMIL et les keyframes CSS ; aucun runtime Lottie sérieux sur Unity/Quest) — **ne pas refaire l'analyse des formats**, tout est dans la carte kanban « Loader animé — presets paramétriques » (`cards/5-planifie/285-…`).
- **🐛 Bug ouvert (relevé le 2026-08-25)** : **la jauge de stockage du menu latéral ne se rafraîchit pas** après lajout (ni la suppression) dune ressource. `QuotaBarsWidget` nappelle `instanceGetQuota` quune fois, dans `initState` (`quota_bars_widget.dart:23`), et il vit dans le pied du menu **persistant** (`main_screen.dart:477`) : le chiffre affiché date de louverture de session. ⚠️ **Le comptage serveur est correct**`storageUsedBytes` est recalculé à chaque appel (`SUM(SizeBytes)`, `InstanceController.cs:383`) et le contrôle dupload refait un `GET /quota` avant de téléverser (`resources_screen.dart:186-198`). Doù lincohérence visible : une jauge à « 0 KB » et un refus 413 sur le même chiffre. Détail dans [todo-features.md](todo-features.md) — Quota stockage.
**Prochaine chose à faire si tu as 1h** : jouer le **§19.13 cas E** du plan de test (chasse au trésor / carnaval) de bout en bout, depuis la création dans manager-app. C'est le scénario le plus complet — il valide SectionMap + SectionParcours + géodéclenchement + questions + mode jeu d'un coup.
---
## 1bis. État des builds — mesuré le 2026-08-06
> Aucune app n'était buildable au début de l'audit. Deux corrections ont débloqué les 3 apps Flutter.
> Détail : **[parity-manager-visitapp.md §6](parity-manager-visitapp.md)** · checklist : **[test-plan.md §0](test-plan.md)**
| Repo | Build | Note |
|---|---|---|
| manager-service | ✅ `dotnet build` et ✅ `dotnet test` **124/124** (2026-08-06) | La remise en route de la suite a révélé un **bug backend réel** : `BuildAuditEntries()` ajoutait un `AuditLog` au contexte *pendant* l'énumération du `ChangeTracker``InvalidOperationException: Collection was modified` sur toute écriture d'entité auditée (Section, Resource, Configuration, Device, User, Instance). Corrigé : énumération matérialisée, logs ajoutés après la boucle. Invisible jusque-là parce que les tests ne compilaient plus |
| manager-app | ✅ `flutter build web` | débloqué : `// @dart=2.18` manquant dans `manager_api_new/lib/api/onboarding_api.dart` (fichier ajouté à la main pour l'onboarding, sans l'annotation de version que porte `api.dart`) — **cassait les 3 apps Flutter d'un coup** |
| mymuseum-visitapp | ✅ `flutter build apk`**les 3 flavors, vérifiés le 2026-08-12** (`dev`, `mdlf`, `fortsaintheribert`) | ⛔ **Le ❌ « revérifié le 2026-08-11 » était périmé.** Le build passe, et sa config Android était **déjà à niveau** — c'est `tablet-app` qui était en retard sur lui, pas l'inverse. K5 s'est donc réduit à deux alignements que le build réclamait en avertissement : NDK 27.0.12077973 → **28.2.13676358** (`speech_to_text` le réclame nommément) et Kotlin 2.1.0 → **2.3.10** (seuil 2.2.20 annoncé par Flutter ; même version que `tablet-app`). L'APK passe de 382 à **349 Mo**. Les 4 erreurs Dart Ray-Ban restent hors du graphe de `main.dart` et ne gênent pas le build (§5bis) |
| visitapp-web | ✅ `npm run build` | débloqué : helper `getGeoPointLatLng()` dans `src/lib/geo.ts`, les 7 erreurs venaient d'un `unknown` non narrowé. ⚠️ ce type d'erreur est **invisible en `next dev`** |
| tablet-app | ✅ `flutter build apk`**réparé le 2026-08-12**, APK produit pour la première fois depuis le 16 avril | ⛔ **Le diagnostic « un seul défaut, purement Gradle, peut-être réglé par `5a6701d` » était doublement faux.** Il a tenu des mois parce que personne n'avait lancé la commande. Réalité : **dix crans d'outillage** (Gradle 7.5 → 8.11.1, AGP 7.2.0 → 8.9.1, Kotlin 1.9.0 → 2.3.10, option AGP supprimée, jcenter mort, heap Gradle 1536M → 4096M, Jetifier coupé, `resolutionStrategy` sur `androidx.lifecycle` retiré) — **corrigés le 2026-08-12****puis 132 erreurs Dart** que le Gradle masquait : l'app est restée sur **l'ancien contrat d'API**, celui d'avant Postgres v3. Détail complet et suite : **[v1-plan.md lot K](v1-plan.md)** |
**Leçon** : `flutter analyze` et `next dev` ne suffisent pas. Seuls `flutter build` / `npm run build` / `dotnet test` disent la vérité — d'où la nouvelle section §0 du plan de test.
**Leçon renforcée le 2026-08-12** : un build **jamais lancé** ne vaut pas mieux qu'un build vert supposé. Trois affirmations de ce tableau ont sauté d'un coup dès qu'on a tapé la commande. Corollaire à retenir : *« à reconfirmer par un vrai build »* dans une doc veut dire **que ce n'est pas confirmé**, et un point non confirmé ne doit pas être chiffré comme une demi-heure.
⚠️ **Et un piège de vérification à connaître** : `flutter build apk … | tail` renvoie **le code de sortie du `tail`**, pas celui du build. Un échec y ressort en « exit code 0 ». Rediriger vers un fichier et lire `$?` séparément.
---
## 1ter. Chantier « migration Postgres v3 » — ordre de bataille (ajouté le 2026-08-07)
> Postgres n'est pas encore en prod. Trois chantiers ont été analysés le 2026-08-07 et découpés selon une règle unique :
>
> **Ce qui touche aux données déjà écrites part avec la migration. Ce qui est purement additif attend.**
>
> Chaque ligne est faite pour être attaquée dans une conversation dédiée : lire le doc, faire l'étape.
>
> ⚠️ Cette section dit **ce qui doit entrer dans le schéma**, pas comment on remplit la base. L'opération de bascule elle-même — et l'état de `MigrationController` — sont au **§1quinquies**.
**Détail complet : [status/postgres-v3.md](status/postgres-v3.md)**
---
## 1quater. Chantier « Guide IA V1 » — ordre d'exécution (arrêté le 2026-08-07)
> Cette section ne décrit **rien de neuf** : elle donne l'ordre dans lequel exécuter ce que trois plans détaillent déjà, parce que le chantier les traverse tous les trois et qu'aucun ne porte la vue d'ensemble.
>
> **Décision : tout est fait d'un coup, sans mise en prod intermédiaire.** Rien ne part en prod avant que le backlog (kanban + ce fichier) soit terminé — voir §1ter, la base Postgres est vide et c'est une fenêtre à ne pas refermer.
**Détail complet : [status/guide-ia-v1.md](status/guide-ia-v1.md)**
---
## 1sexies. Ordre d'exécution de toute la V1 — et le lot design (ajouté le 2026-08-11)
> Ce fichier dit **ce qu'il reste**, par domaine. Le kanban dit **où en est chaque chantier**. Ni l'un ni l'autre ne disait **dans quel ordre**, ni ce qui bloque quoi.
>
> → **[v1-plan.md](v1-plan.md)** — 9 lots (A à I plus D-bis), 18 liens de dépendance, chemin critique, arbitrages en attente. **4 à 5 semaines**, portées à 5-6 avec le lot design.
**Détail complet : [status/ordre-execution-v1.md](status/ordre-execution-v1.md)**
---
## 1quinquies. La bascule elle-même — Mongo → Postgres en prod (ajouté le 2026-08-09)
> **Trou identifié le 2026-08-09** : le §1ter dit *ce qui doit entrer dans le schéma avant que la base se remplisse*. Il ne dit nulle part **comment on remplit la base**. L'opération de bascule n'était écrite ni ici, ni dans le kanban, ni dans aucun plan de `v2/`.
>
> Rappel : la prod tourne **encore sur MongoDB**. Le transfert se fait par `MigrationController` (`POST /api/migration/run`, paramètres `dryRun` et `instanceId`), qui rejoue Instances → Resources → Users → Configurations → ApplicationInstances → Sections → Devices, puis relie les menus et enrichit les ressources PDF.
**Détail complet : [status/bascule-prod.md](status/bascule-prod.md)**
---
## 2. Tests
→ Détail complet : **[test-plan.md](test-plan.md)**
Checklist manuelle organisée en 20 sections + non-régression. À cocher au fur et à mesure avant une mise en prod. Section "Hors scope" = features non implémentées, pas testées.
Ordre conseillé :
0. **§21 — Visite hors ligne** (ajouté le 2026-08-07) : bugs suspectés d'après le code, à confirmer sur device avant de planifier le chantier
1. **§0 — Porte d'entrée build** (5 commandes, bloquant absolu)
2. **§19 — Parité manager-app → apps visiteur**, par type de section : c'est le chapitre qui répond à « tout ce que je configure est-il visible ? ». Le §19.13 (SectionParcours, 5 cas d'usage) est le plus à risque.
3. §18 — Onboarding self-service (jamais exécuté)
4. §1-17 — features par lot, §16 non-régression
---
## 2bis. Parité manager-app → apps visiteur
**[parity-manager-visitapp.md](parity-manager-visitapp.md)** (audit du 2026-08-05, sur le code réel)
- **Couverture par type de section : complète** — les 13 `SectionType` sont dispatchés dans `mymuseum-visitapp` et `visitapp-web`, aucun écran vide.
- **17 écarts au niveau champ** au moment de l'audit : 7 côté mobile, 10 côté web. Le plus lourd — l'absence d'assistant IA côté web, alors que le plan Essentiel est web-only — est **résolu le 2026-08-06**, et répercuté sur mobile le même jour.
- Suivi des corrections : table du **[test-plan.md §20](test-plan.md)**.
**Détail complet : [status/parite-manager-visitapp.md](status/parite-manager-visitapp.md)**
---
## 3. Sécurité & dette technique
→ Audits : **[security/audit-securite-manager-service.md](security/audit-securite-manager-service.md)** et **[security/audit-manager-app.md](security/audit-manager-app.md)** (2026-07-13/14)
- **manager-service** — le point critique est la **rotation des secrets committés** (JWT signing key, clés API, connection strings, MQTT, Telegram). Gravité inchangée, mais **déplacée en fin de backlog le 2026-08-11** : les 6 repos sont privés sur un Gitea auto-hébergé. ⚠️ Contrainte à ne pas perdre — **la rotation doit précéder I3** (compose/Traefik prod), sinon la prod naît avec les clés de l'historique et il faut rotationner deux fois. Puis quatre niveaux Haute → Basse.
- **manager-app** — le critique (`manager_api_new` désynchronisé, 68 erreurs de bruit) est ✅ **résolu le 2026-08-11** : `flutter analyze` passe à 3 issues et redevient un feu vert utilisable. Au passage, les 3 déclencheurs de génération OpenAPI ont été supprimés, ce qui **verrouille la règle « `manager_api_new` s'édite à la main »**. Reste Haute → Basse : duplication du shell de dialog, `build()` monolithiques, i18n contournée, clé AES en dur. → **La duplication du shell de dialog a désormais sa maquette et sa carte** : `DOCS/claude design/refonte-configuration-sections.html` §6 (coquille, inventaire des 29 dialogues, deux dialogues dessinés) et `kanban/cards/5-planifie/290-…`. ⚠️ **Ordre imposé** : les 8 `showNewOrUpdate*` sont *absorbés* par l'éditeur de collection du gabarit B — extraire un scaffold pour eux d'abord, ce serait refactoriser du code à supprimer. Voir la note dans `security/audit-manager-app.md`.
**Détail complet, par priorité et avec les cases à cocher : [status/securite-dette-technique.md](status/securite-dette-technique.md)**
---
## 4. Infrastructure & Ops
- [x]**Travail non committé — résorbé. Revérifié le 2026-08-11 (2e passe) : plus rien en attente.** Les **9 repos** (DOCS compris) sont propres (`git status --porcelain` vide partout) **et à jour avec leur remote** (aucun `ahead`/`behind`). L'alerte du 2026-08-09 et sa révision du matin du 11/08 (« ~41 fichiers en working tree dont 9 non suivis ») sont toutes deux **périmées** : le lot 3 RAG est dans `18e4240`, le socle visuel + Guide IA dans `eb8e849`. **L'étape 1 du lot A est terminée** — ne pas la re-planifier
- Derniers commits par repo : `manager-service` `18e4240` (RAG : pipeline d'ingestion, endpoints du guide IA, journalisation RGPD) · `manager-app` `eb8e849` (socle visuel et écran Guide IA à deux onglets) · `visitapp-web` `473fc3a` · `mymuseum-visitapp` `af44927` · `tablet-app` `5a6701d` (drapeaux du migrateur Flutter) · `myinfomate-landing` `d91cf7f` · `unov-landing` `cfa70c4` · `OpenGlasses` `1d887d7`
- Rappel de l'état des branches : `manager-service``MyInfoMate_3.0`, `manager-app``Interface-refacto`, `mymuseum-visitapp``Meta-Rayban-Test` (23 commits d'avance sur `master`, voir §5bis), `tablet-app``AI-Assistant-test`, `visitapp-web` et les deux landings → `master`
- [ ] **pg_dump quotidien** pour `manager-service` (Postgres `my_info_mate`), en complément du snapshot OVH. ⚠️ **Bloquant au moment de la bascule** : point 18 du [§1quinquies](#1quinquies-la-bascule-elle-même--mongo--postgres-en-prod-ajouté-le-2026-08-09), le seul filet du jour J
- 🔨 **Scripts livrés et testés le 2026-09-07**`manager-service/ManagerService/Deployment/backup/`, installation et restauration documentées dans `README.md` et **`RESTORE.md`**. Cycle complet validé sur la base de dev : dump → vérification de relecture → restauration dans une base jetable → **25 tables aux comptages identiques**. Extraction par instance validée aussi (les deux extractions totalisent exactement le global). Embeddings exclus : dump de 12 Mo → **1,7 Mo**
-**Destination créée le 2026-09-07** — projet GCP `myinfomate-backups` (séparé de `mymuseum-3b97f`), bucket `gs://unov-myinfomate-backups` en `EU` multi-région, versioning activé, accès public interdit, lifecycle 400 jours sous `pg/` seulement. Service account `backup-writer` en `objectCreator` + `objectViewer` : **incapable de supprimer, et vérifié** (l'écriture passe, le `rm` renvoie `403 storage.objects.delete denied`). Détail et test rejouable dans `Deployment/backup/README.md`
-**Ce n'est pas encore une sauvegarde** : `rclone` n'est ni installé ni configuré sur le VPS, donc rien ne part encore hors site. Et `notify.sh` n'est branché sur aucun canal — carte kanban « Brancher `notify.sh` sur un canal réel »
- 📌 **Volume des médias : 1,43 Go** (`gcloud storage du -s gs://mymuseum-3b97f.appspot.com`, 2026-09-07). Le bucket connaissait la réponse que le plan attendait du backfill. Assez petit pour tenir dans le même bucket de sauvegarde sous `media/`**pas besoin d'OVH Object Storage**, la disposition croisée envisagée le 07/09 est abandonnée pour cette raison
-**Versioning activé sur le bucket des médias le 2026-09-07**`versioning_enabled: True` sur `gs://mymuseum-3b97f.appspot.com`, plus une lifecycle rule qui purge les versions périmées à 30 jours (le bucket n'avait **aucune** règle auparavant, rien n'a été écrasé). L'ordre importait : le filet est en place **avant** que `Firebase:StorageBucket` soit renseignée (carte « Poser `Firebase:StorageBucket` »), jour où la suppression de blob devient réelle. Le bucket portait déjà une soft delete de 7 jours depuis mars 2024
- 📌 **Décision de conception** : **un dump global, pas un par client.** Une restauration par client se fait par extraction depuis le dump global (`extract-instance.sh`) — 14 tables portent `InstanceId`, 7 tables filles se rattachent par leur parent. Des sauvegardes par client multiplieraient par N les jobs à planifier, les échecs à surveiller et les restaurations à tester, pour la même capacité de récupération
-**Médias copiés le 2026-09-07** — 1,43 Go, 2407 objets, `gcloud storage rsync` serveur à serveur vers `gs://unov-myinfomate-backups/media/`. **Taille identique à l'octet**, zéro erreur. C'est la première sauvegarde des médias depuis le début du projet. ⛔ **Pas encore planifiée** : c'est une copie unique. **Storage Transfer Service envisagé puis écarté le 2026-09-07** — 5 attributions IAM et un agent de service avec droit de suppression, pour automatiser une commande à relancer toutes les quelques semaines sur des données qui bougent de ~2 ressources par mois : disproportionné. Le mécanisme reste le `rsync`, qui tourne avec un compte humain sans aucun changement de permission, et sera ajouté aux timers du VPS avec la sauvegarde Postgres
- 🔍 **L'inventaire des orphelins est à moitié fait, gratuitement** — 27 objets sur 2408 n'appartiennent à aucun client : 19 sous un id à un caractère de celui de MyInfoMate (`…f1` vs `…f2`), 2 sous MNAHA (qui n'existe qu'en base de dev), et 6 fichiers de test à la racine du bucket (`giphy.gif`, une vidéo Cybertruck, des `file_example_*`) qui ne suivent pas le schéma `pictures/{instanceId}/{id}` et sont donc invisibles au quota. Détail et réserves dans [v2/media-storage-plan.md](v2/media-storage-plan.md)
-**Deuxième chose qu'il débloque** : le job `visit-events-purge` (purge des stats au-delà de 13 mois) est codé et enregistré mais **volontairement inactif** — il ne supprimera rien tant que `Stats:RetentionDays` n'est pas défini. Ne l'activer (`Stats__RetentionDays=395`) qu'une fois ce pg_dump réellement hors site
- [ ] **Fermer le port PostgreSQL exposé publiquement** via Traefik/Docker (détail dans [todo-features.md](todo-features.md), section "Infrastructure — Sécurité réseau") — accès DBeaver via tunnel SSH uniquement
- [x] 🔨 **Sauvegarde de la prod Mongo — faite le 2026-09-07.** `Deployment/backup/dump-mongo-prod.sh`, lecture seule (aucun `mongorestore` dans le script, volontairement). L'export de référence datait du 1er avril. Delta sur 5 mois : 11 sections créées, 3 supprimées, 17 modifiées, 9 ressources créées. **VisitNamur crée du contenu, MDLF retouche l'existant, Fort Saint Héribert n'a rien touché** — utile à savoir avant de reprendre contact avec eux. ⚠️ Piège : le formatage des dates change entre versions de `mongoexport` (`.420Z``.42Z`) et fait apparaître 239 fausses modifications ; normaliser avant de comparer
- [ ] **Backup Mongo automatique en cron** — le script existe désormais, reste à le planifier. Voir [todo-landing-deploy.md](todo-landing-deploy.md), section 5 (dump + `docker cp` vers l'hôte, pas seulement dans le conteneur)
- [x] Déploiement landing page (`docker-compose-landing.yml` séparé, ne jamais toucher au compose principal) — voir [todo-landing-deploy.md](todo-landing-deploy.md) pour les règles à respecter
---
## 5. Roadmap produit — nouveaux canaux & IA
Dix-neuf chantiers de canaux et d'IA, chacun avec son plan détaillé. État au 2026-09-02 :
- **V1** — **visiteur web** : `visitapp-web` build ✅, les 13 types de section rendus, assistant IA inclus ; **il ne manque que le Dockerfile et l'entrée `app.myinfomate.be` au compose**. Bloquant, le plan Essentiel vendu par l'onboarding est web-only. Plus l'**écran Guide IA**. La **Médiathèque** (basculée en V1 le 2026-09-02) est **codée de bout en bout le même jour** — index inverse des usages, panneau de détail, facettes, suppression protégée, les deux bugs corrigés ; `dotnet test` 236/236 et `flutter build web` verts, **mais la checklist de 15 points reste à passer dans un navigateur** (colonne « À tester » du kanban). Voir [v1-mediatheque-plan.md](v1-mediatheque-plan.md).
- **Livrés** — export PDF des stats à la demande, historique 13 mois pour tous les plans, refonte de l'écran Statistiques (⚠️ jamais ouvert dans un navigateur).
- **V2** — module Studio (génération IA d'images), Personnages (une entité `Persona` unifiant trois plans), TTS pré-généré, kiosk web, VR Meta Quest, import IA + RAG. 🛠️ **Studio : lots 0 à 6 codés et commités (branche `studio`, pas encore poussée), calibrage §9.4 fait le 2026-09-15** (≈ 3 $ réels, unité de crédit arrêtée : 1 crédit = 0,015 $ de coût fal, prix de vente avec marge). Lot 7 : échantillons de style et figures d'archétypes embarqués, reste l'écoute des voix. **Lots 8 et 8bis codés et commités le 2026-09-15, jamais exécutés en réel** — narrateurs assignables et chaîne de résolution, génération audio Gemini TTS → MP3 (1 crédit par minute, état « à régénérer »), onglet Narrateurs du parcours, blocs sur l'article et le point de carte, cartes Narration et Personnages dans l'écran Configuration, portrait du narrateur et écran « les personnes que vous allez rencontrer » sur les trois apps visiteur, lecteur audio dans la fiche d'un point de carte (lot 8e, ajouté le même soir). ⚠️ Au passage, les apps visiteur ne jouaient pas un audio rangé par identifiant de ressource — corrigé. **À faire** : passer [test-plan-studio-narration.md](test-plan-studio-narration.md) (clé Gemini + ffmpeg), extraits d'écoute des voix, test du conservateur (gate lot 4), push de la branche `studio`. Le prérequis « le backend ne sait pas écrire dans le bucket » est levé depuis le lot 0.
- 📄 **La frontière entre le Studio IA et le canal XR est tranchée depuis le 2026-09-11** : [v2/immersif-frontiere-plan.md](v2/immersif-frontiere-plan.md). Les deux plans se renvoyaient la balle (le Studio mettait la 3D en « dépend du chantier VR », le plan VR décrivait les POI sur GLB sans dire d'où venait le GLB). Trois couches : ressources immersives (360/GLB, **se vendent sans casque**), consommation, et le Studio comme *source d'approvisionnement* de la première. **Cinq décisions de rétro-compat touchent des lots antérieurs à la 3D** — à lire avant la première migration du Studio, pas en arrivant au lot 11.
-**Le backend du canal XR est fait le 2026-09-11** (lot `XR-1` de [v2/vr-quest-unity-plan.md](v2/vr-quest-unity-plan.md)) : `Device.AppType` + migration, `DeviceController` résout l'`ApplicationInstance` sur le canal au lieu de `Tablet` en dur, `Get` filtre par canal, `ApiKeyAppType.VrApp`, `appType` dans `manager_api_new`. 224 tests au vert. Et **`XR-3` était déjà fait** — l'export est ouvert aux apps depuis `9cc45c5`, le plan était en retard sur le code.
-**`XR-2` fait le 2026-09-12 — le canal VR est administrable.** L'écran XR est une coquille à deux sous-onglets : « Configuration » réutilise `AppConfigurationLinkScreen` tel quel (déjà générique sur l'`appType`, zéro code neuf), « Casques » est la grille avec pincode d'appairage, état connecté, batterie, version et dernier vu. i18n FR/EN/NL. ⚠️ Deux affirmations du plan étaient fausses : la grille kiosk liste des `AppConfigurationLink`, pas des `Device` (le filtre par canal était déjà implicite, et le paramètre `appType` de `/api/device` ne sert pas à cet écran), et batterie/version/dernier vu ne sont que dans `DeviceDetailDTO` — donc un appel de détail par casque, assumé sur une flotte qui se compte en unités. Ne restent que deux puces documentaires de `XR-3` (contrat d'export versionné, décision multilingue).
- ⚠️ **Nommage** : les lots du chantier XR s'appellent `XR-1``XR-5` (renommés le 11/09, ils s'appelaient `V-1``V-5` et se confondaient avec « V1 »). Les lots du backlog V1 sont lettrés `A``J` dans [v1-plan.md](v1-plan.md).
-**L'app Unity tourne sur un vrai casque depuis le 2026-09-11** (lot `XR-4`, items `E0` et `E1` sauf l'Interaction SDK) : Unity 6000.0.83f1, projet URP dans [`vr-app/`](../vr-app/README.md), APK sideloadé sur un Quest 2 en Horizon OS v207. **Le prérequis qui bloquait tout le lot est levé.** Mesuré sur le casque : un décor glTF de 50 Mo charge en **1,4 s** et tient **72 FPS**, mais avec le GPU au maximum — le budget de scène devra être chiffré. La **conversion d'axes glTF→Unity est prouvée** (`NegateX`), au repère de calibration, donc elle ne se redémontre plus. - ⚠️ **Deux numérotations E0-E<n> se confondaient** : celle de `XR-4` (E0-E11, l'app Unity complète) et celle du plan de la piste Scène 3D. La seconde est renommée **S0-S8** le 11/09 — [vr-app/docs/04-phase3-plan.md](../vr-app/docs/04-phase3-plan.md). **Le plan unifié gouverne** ; en cas de contradiction, le document le plus récemment mis à jour tranche. Fait dans la piste S : S0, S1 (sauf le cercle de navigation), S2 ; S3 (viewer web) écrit mais jamais essayé. - ➡️ **Prochain travail sur le canal XR** : le POC de `XR-4`, soit `E2` (appairage par pincode, `POST /api/device`) → `E3` (lecture de `GET /api/configuration/{id}/export`) → `E5` (menu flottant) → `E6` (360°). E2 et E3 sont débloqués par XR-1 et XR-3, tous deux faits.
- ⚠️ **Deux numérotations E0-E<n> se confondaient** : celle de `XR-4` (E0-E11, l'app Unity complète) et celle du plan de la piste Scène 3D. La seconde est renommée **S0-S8** le 11/09 - [vr-app/docs/04-phase3-plan.md](../vr-app/docs/04-phase3-plan.md). **Le plan unifié gouverne** ; en cas de contradiction, le document le plus récemment mis à jour tranche. Fait dans la piste S : S0, S1 (sauf le cercle de navigation), S2 ; S3 (viewer web) écrit mais jamais essayé.
- 🟡 **`E2` et `E3` écrits le 2026-09-12, jamais essayés** : `vr-app/.../Net/``ApiClient` (le seul point de contact avec le backend), `PairingService` (pincode → clé d'API → `POST /api/device` avec `appType = 3`, appairage persistant), `ConfigurationExport` (un appel pour tout le contenu), et `PairingBootstrap` qui affiche le résultat dans le casque. Compile contre les DLL Unity + Newtonsoft ; **rien n'a encore parlé à un vrai serveur ni tourné sur le Quest**. ⚠️ Confirmé au passage : l'export **ne rend qu'une langue** (`GetReferencedResourceIds(language)`), donc changer de langue à chaud impose de recharger — c'est la question ouverte de XR-3. ⚠️ Et les scripts Unity parlaient encore `E1`/`E2` pour `S1`/`S2` : renommés `S1Bootstrap`/`S2Bootstrap` le 12/09.
- 🟡 **`E4` (cache offline) et `E9` (télémétrie) écrits le 2026-09-12, jamais essayés** : `ContentCache` (écriture atomique — une borne débranchée le soir ne laisse pas de JSON tronqué), `ContentService` (**cache d'abord**, rafraîchissement de fond dont l'échec est silencieux), `Telemetry` (« tire et oublie »). ⚠️ La route des stats est **`/api/stats/event`**, et `appType` y part en **chaîne parsée par nom** : une faute de frappe retombe sur `Mobile` sans erreur, et les visites du casque seraient comptées en mobile.
- 🟡 **`E5` — le menu flottant, écrit le 2026-09-12, jamais essayé** : arc de panneaux à 2,2 m qui **ne suit pas la tête**, vignettes chargées après coup, télémétrie branchée, et **trois moyens de viser** — manette (gâchette), main (pincement), tête (temporisation 1,2 s). ⚠️ **« À la tête », pas « aux yeux »** : le Quest 2 n'a pas d'eye tracking, seul le Quest Pro en a. ⚠️ **La dépendance `E5 → E1` du plan était fausse** : `OVRInput` et `OVRHand` viennent du **Core** SDK, déjà installé — l'Interaction SDK n'apporte que le confort. **E1 sort du chemin critique du POC** (voir le §8bis du plan).
- 🟡 **`E8` — quatre types de section, écrits le 2026-09-12, jamais essayés** : Slider, Map, Parcours et Event, tous dans la même vue paginée (`PagedView` + `SectionPages`) — une chose à la fois, grande, devant soi. ⚠️ **Deux écarts assumés** : la **Map est une liste de POI**, pas la maquette 3D promise (ça demande `SectionModel3D`, et une carte Google sur panneau flottant serait *moins* bonne qu'un téléphone) ; le **Parcours** est rendu comme une Map, pour la même raison. L'**Event** ne montre que la journée en cours et retombe sur les dates suivantes si elle est vide. Une section non gérée affiche son titre et le dit.
- 🟡 **`E10` — la session de borne, écrite le 2026-09-12, jamais essayée** : fin de visite à la **repose du casque** (via `CommonUsages.userPresence` d'Unity XR, donc sans SDK Meta ni rien à configurer) ou après **90 s sans activité**, retour au menu **recentré devant le visiteur suivant**, nouvelle session de stats, écran qui ne s'endort pas. ⚠️ Le verrouillage de l'app sur le casque n'est **pas** du code : c'est le mode appareil de Meta.
-**La ressource 360° est faite le 2026-09-12** — le dernier prérequis du POC est levé. `ResourceType.Image360` (11), `Video360` (12), `Model3D` (13) **en fin d'enum**, sans migration (colonne déjà `integer`), verrouillées par un test valeur par valeur. Côté manager : la pastille de type du sélecteur de médias devient **cliquable** pour déclarer une 360 (aucune extension ne le dit — un panorama est un `.jpg` comme un autre), le `.glb` est accepté et déduit seul, i18n FR/EN/NL. ⚠️ **Le vrai piège n'était pas l'enum mais la compression** : `ImageCompressor` ramène toute image à 2560 px, ce qui ferait d'une équirectangulaire 8192×4096 une bouillie dans un casque — les trois types en sont exclus, sur les **deux** chemins d'upload. ✅ Et le prérequis « le backend ne sait pas écrire dans le bucket » **ne s'applique pas** : `manager-app` pousse directement dans Firebase puis enregistre l'URL.
- 🟡 **`E6` — la lecture 360°, écrite le 2026-09-12, jamais essayée** : image et vidéo équirectangulaires dans le même shader `Skybox/Panoramic`, la vidéo par une `RenderTexture` alimentée depuis le cache disque, et le ciel d'origine restauré à la fermeture (c'est un réglage global). **Le retour au menu ne reste pas affiché** — 4 s au début, puis il s'efface et revient quand le visiteur baisse les yeux ou prend une manette, et il est reposé sous le regard courant : en 360 on tourne sur soi-même. ⚠️ **`Skybox/Panoramic` doit être dans *Always Included Shaders*** — sinon le ciel sort **magenta sur le casque et correct dans l'éditeur**, le piège déjà payé avec glTFast. ⚠️ **Mémoire** : `LoadImage` décode en RGBA32, soit **134 Mo pour une 8192×4096** — compressée en ASTC au chargement et libérée à la sortie, sinon l'app se fait tuer au bout de quelques 360 (invisible sur un essai unique, fatal en borne).
- 🟡 **`XR-5` — la supervision de flotte, l'essentiel écrit le 2026-09-12** : `PUT /api/device/{id}/heartbeat` (batterie, version ; le serveur pose lui-même `Connected` et `LastSeen` — recevoir le battement *est* la preuve) et `FleetReporter` côté casque, toutes les 3 min plus une fois au démarrage. **Battement HTTP plutôt que MQTT** : à 1-3 casques, MQTT coûterait dix fois plus de code pour remonter trois chiffres ; il redeviendra utile pour **pousser** vers le casque, pas pour remonter. ⚠️ `Update` n'écrivait **ni `AppVersion` ni `LastSeen`** : les deux colonnes que l'onglet XR affiche n'avaient aucun chemin d'écriture.
- 🔴 **Trois bugs de production trouvés et corrigés le 2026-09-12, tous sur le même mur** : outre l'appairage ci-dessous, `GET /api/Device/{id}/detail` était fermé aux clés — or c'est le **premier appel d'une tablette qui démarre**, celui qui lui dit quelle configuration afficher : une tablette déjà appairée ne retrouvait plus son contenu au redémarrage. Et `tablet-app/lib/main.dart:38` reconstruisait son client **sans clé**, alors qu'elle est persistée en base locale. La route refusait les clés, l'app n'en envoyait pas : les deux se tenaient.
- 🔴 `POST /api/device` répondait **403 à toute tablette** depuis le **13/03/2026** — la policy `InstanceAdmin` posée sur `DeviceController` contre une clé d'API qui ne porte que `AppRead`/`Viewer`, et `tablet-app` ne s'authentifie jamais autrement. Une tablette déjà appairée ne rappelle jamais cette route : **seul un nouvel appareil échouait**, donc personne ne l'a vu pendant six mois. ⚠️ **À vérifier sur le terrain** — les tests appellent le contrôleur directement, sans filtre d'autorisation, donc ils ne prouvent rien sur l'accès.
- 🟡 **`E7` — la maquette 3D et ses points d'intérêt, écrite le 2026-09-12, jamais essayée.** `SectionModel3D` est **un vrai type de section**, pas une Map augmentée : une maquette n'a ni fond cartographique, ni zoom, ni coordonnées terrestres. Ce qu'on réutilise, c'est le **`GeoPoint`** — il portait déjà `SectionMapId` *et* `SectionEventId`, un troisième rattachement suit le patron, et les points gardent titre, description, audio et multilingue. Migration `AddSectionModel3DAndPoiPosition` (3 colonnes nullable). ✅ **L'« éditeur de placement 3D », annoncé comme le seul morceau non trivial, n'était pas à écrire** : `vr-app/viewer` sait déjà le faire et **son protocole `postMessage` existait déjà**, pensé pour ce cas — il parle en manifeste de scène, le même objet que lit le casque. Le manager le monte en **iframe**, comme il le fait déjà trois fois ailleurs. Côté casque, `Model3DView` ne dessine rien non plus : il construit un `SceneManifest` depuis la section et le passe au `SceneBuilder` de la piste S. ⚠️ **Le viewer n'a jamais tourné** (S3), et il doit être servi sous le même domaine que le manager.
- 🔴 **Deux trous de l'export trouvés et corrigés le 2026-09-12** — et ils ne concernaient **pas que la VR**, l'export est ce qui part en visite hors ligne. **Un** : il ne portait **aucun champ spécifique de section** (`Section.ToDTO()` n'est pas virtuelle, l'export l'appelait sur une variable de type `Section`) — une galerie sans ses images, une carte sans ses points. **Deux** : les collections des sous-types n'étaient pas chargées, donc une Map exportait zéro point. Corrigés par `SectionFactory.ToDTO` + pré-chargement, verrouillés par six tests. ✅ Au passage, la question multilingue de `XR-3` se tranche toute seule : `language` ne filtre que les **ressources**, l'omettre rend toutes les langues — le casque change donc de langue **sans aucun appel**.
-**`XR-3` est clos le 2026-09-12** : le JSON d'export est un **contrat versionné** (`exportVersion: 1`, `generatedAt`, règles de compatibilité écrites sur le DTO), et la question multilingue est tranchée par le code lui-même.
-**`XR-5` est clos aussi** : **pousser une configuration vers le casque sans MQTT** — la réponse au battement porte la configuration assignée, le casque compare et recharge en revenant au menu, sans redémarrage. Coût : trois minutes de latence au pire. Ce que ça évite : une connexion permanente sur une borne et une bibliothèque MQTT dans le build IL2CPP. Le publish serveur existe déjà si un jour trois minutes sont trop. Plus l'**alerte « casque muet »** après une heure sans battement — elle corrige un mensonge, la pastille « connecté » venant du dernier battement reçu.
- ➡️ **Prochain travail sur le canal XR** : **jouer le §25 du [plan de test](test-plan.md)** — 11 blocs, **135 cas**. Le §25.7 (déclarer une 360 dans le manager) et le §25.9.7 (appairer une nouvelle tablette) se jouent **sans casque** ; le reste demande le Quest. **Le POC est écrit en entier** : rien de E2 à E10 n'a jamais tourné, et c'est la seule chose qui manque.
- **Abandonné le 2026-09-02** — talking head : son lipsync reposait sur `enable_time_pointing` de Google Cloud TTS, or le code livré tourne sur Gemini TTS. Le plan était sans fondation, pas « à faire ».
**Détail complet, la table des 19 chantiers : [status/roadmap-canaux.md](status/roadmap-canaux.md)**
---
## 5bis. Lunettes Ray-Ban Meta — état réel du code (relevé le 2026-08-09)
> Cette section corrige une erreur de ce fichier : la roadmap classait l'AR lunettes en « pas commencé, pilote subventionné quand le SDK sort de preview ». **Un POC fonctionnel existe depuis mai-juin 2026**, ~2 700 lignes de Dart dans `mymuseum-visitapp`, sur la branche **`Meta-Rayban-Test`** (23 commits d'avance sur `master`, jamais mergée — c'est la branche de travail courante du repo).
**Détail complet : [status/rayban-meta.md](status/rayban-meta.md)**
---
## 5ter. Visibilité — SEO/GEO de la landing & LinkedIn (ouvert le 2026-09-10)
→ Plan en 21 actions et suivi : **[myinfomate-landing/audit-seo-geo.md](../myinfomate-landing/audit-seo-geo.md)** (§0) · posts : **[linkedin-posts-lancement.md](linkedin-posts-lancement.md)**
-**Corrections rapides (actions 1 à 5)** faites le 2026-09-10 : titres, schema `Organization` (adresse, TVA), 3 offres dans le schema, 9 modules dans le HTML, liens morts. Build OK, vérifié dans le HTML servi. **Stagées, pas committées.**
-**LinkedIn** : vitrine MyInfoMate créée sous Unov, fiche produit soumise à LinkedIn.
-**Actions 6 à 8** faites le 2026-09-10 : `llms.txt` en français, **prérendu statique rétabli** (58 pages, le layout racine est désormais `[lang]/layout.tsx`), page **`/{lang}/tarifs`** avec FAQ tarifaire. Stagées, pas committées.
-**`/assistant-ia` livrée le 2026-09-10 dans les 4 langues** (relue par Thomas en FR) — 6 FAQ par langue, `hreflang` complet. **62 pages prérendues, 54 URLs au sitemap.** Au passage, les liens légaux des footers sont passés en `<Link>` : le lint est à **0 erreur** (15 avant).
-**CGU publiées le 2026-09-10** sur `myinfomate.be/conditions-generales` — le lien de la case de consentement de `/signup` pointait jusque-là vers `/{lang}#`, c'est-à-dire nulle part. Lien ajouté aussi dans les pieds de page des 4 langues et dans le sitemap. **Deux contradictions corrigées dans [cgu-myinfomate.md](cgu-myinfomate.md)** : §6.1 citait Starter/Standard (2 et 10 Go) et §7.1 réservait l'IA aux « plans Standard et Premium ». Ce fichier reste la source ; la page Next en est le rendu, à tenir synchrone.
- ⚠️ **Reste dû : la validation juridique des CGU** (jamais faite, le document le dit depuis avril) et le **DPA** qu'un acheteur public demandera. Carte kanban dédiée ; à faire **avant** d'ouvrir l'inscription à de vrais clients payants, pas avant la mise en prod technique.
-**Vocabulaire acheteur injecté dans les 6 segments, en français, le 2026-09-10** — le site n'écrivait « RGPD », « marché public », « accessibilité » ni « médiation numérique` nulle part. Ajouté par segment : une FAQ **RGPD** (les 6), une FAQ **marchés publics** (5 — pas les hôtels, secteur privé), une FAQ **accessibilité** disant franchement qu'il n'y a **ni certification Access-i ni audit** (3 segments publics). L'argument central : Visit Namur, retenu sur **cahier des charges publié**.
-**Trois chantiers techniques bouclés le 2026-09-10** : **FAQ de la page d'accueil** (8 questions ×4 langues + `FAQPage` — la page qu'un LLM atteint en premier n'en avait aucune), **`BreadcrumbList`** sur segments, cas clients, tarifs et assistant IA, et **vraies dates dans le sitemap** (toutes les URLs portaient la date du build ; constante `LAST_MODIFIED` en tête de `sitemap.ts`, à mettre à jour avec le contenu).
-**FAQ des segments traduites le 2026-09-10** : 42 items en EN/NL/DE, parité atteinte dans les 4 langues. Et **images recompressées** — 1,1 Mo économisé sur trois fichiers (`sharp` en installation temporaire, **pas** ajouté aux dépendances ; l'ajouter en dépendance améliorerait l'optimisation d'images en prod, mais c'est une décision de déploiement, à trancher avec le Dockerfile).
-**Captures d'écran de la landing renouvelées le 2026-09-10** — Thomas a fourni les vraies captures de la V3 (app du Fort) : accueil sombre en grille bento et fiche article dans le hero, liste des sections dans le mockup « white label ». Les textes alternatifs, jusque-là en anglais et décrivant l'ancienne interface, sont réécrits et **localisés dans les 4 langues** (`imgAlt` dans `translations.ts`).
- ⚠️ `public/google-maps-route.png` (699 Ko) n'est **référencé nulle part** dans `src/` — supprimable, pas supprimé. ⚠️ Le hero utilise encore `map_generated.png`, une carte **générée**, pas une capture réelle.
-**Trois pages de plus le 2026-09-10, en 4 langues** : `/marches-publics` (argument : Visit Namur attribué sur cahier des charges publié), `/borne-kiosk` et `/visite-hors-ligne`. Gabarit partagé `components/ContentPage.tsx`. Le pied de page a désormais une colonne « Solutions » qui lie les 5 pages produit. **75 pages prérendues, 67 URLs au sitemap.**
-**`sharp` ajouté aux dépendances, sur mesure et non sur croyance** : sans lui `/_next/image` renvoyait le JPEG source (64 Ko), avec lui du WebP (26 Ko). ⚠️ **À confirmer au premier build Docker** : le lockfile a été généré sous Windows et sharp installe des binaires par plateforme ; si `npm ci` sur Alpine ne prend pas la variante `linuxmusl`, il faudra une ligne dans le Dockerfile. Le Dockerfile n'a **pas** été modifié.
-**Pages légales, blog et comparatifs livrés le 2026-09-10.** Légales : mentions et confidentialité traduites en 4 langues, CGU laissées en français avec avis traduit (un juriste va les corriger, traduire maintenant serait à refaire) ; les 3 pages passent sous `/{lang}/…` et les anciennes URLs redirigent vers la langue du visiteur. Blog `/fr/ressources` avec 2 articles. **Comparatifs** `myinfomate-vs-smartify` et `-vs-stqry`, **prix relevés à la source le 10/09 et datés sur la page**. **89 pages prérendues, 81 URLs au sitemap.**
- ⏸️ **Témoignages clients écartés par Thomas** : il ne veut pas solliciter les clients pour l'instant. À rouvrir si un client en propose un.
- ⏭️ **Reste au plan SEO** : deux pages XR à écrire quand les modules seront démontrables — **`/lunettes-connectees` et `/casque-vr`**, cette dernière ajoutée le 10/09 à la demande de Thomas.
- ⚠️ **Les prix des concurrents vieillissent** : la page comparative affiche sa date de relevé. À revérifier avant toute campagne qui s'appuie dessus. ⚠️ Toujours aucun document d'appel d'offres (attestations fiscale/ONSS, DPA, attestations de bonne exécution) : ne rien promettre à ce sujet.
- ⚠️ **Posts LinkedIn en deux vagues** : les posts sur des déploiements réels peuvent partir maintenant ; ceux qui poussent à l'inscription (IA, prix, essai) attendent la bascule prod et un onboarding testé.
---
## 6. Sous-projets MyInfoMate
- **MyInfoMate Sport** — pilote Hockey Namur. Vue d'ensemble : [sport/solution-overview.md](sport/solution-overview.md), spec : [sport/myinfomate-sport-spec.md](sport/myinfomate-sport-spec.md), pitch : [sport/pitch-commercial.md](sport/pitch-commercial.md)
- **MyInfoMate Crèche** — pilote childcare en cours. Vue d'ensemble : [creche/solution-overview.md](creche/solution-overview.md), spec : [creche/myinfomate-creche-spec.md](creche/myinfomate-creche-spec.md), pitch : [creche/pitch-commercial.md](creche/pitch-commercial.md)
---
## 6bis. Cohérence de la doc — audit du 2026-08-07
Passage en revue de tous les fichiers `DOCS/` (hors `sport/` et `creche/`). Corrigé directement :
| Fichier | Ce qui était faux |
|---|---|
| [roadmap.md](roadmap.md) | Annonçait **Claude API** comme LLM à 4 endroits — c'est **Gemini 2.5 Flash-Lite**. Quota IA en requêtes → tokens |
| [parity-manager-visitapp.md](parity-manager-visitapp.md) | L'intro du §6 disait « visitapp-web reste cassé » et B2 « tests backend ❌ » alors que les deux sont corrigés depuis le 2026-08-06 — le doc se contredisait lui-même |
| [v2/tts-pregenerated-plan.md](v2/tts-pregenerated-plan.md) | Chemin Firebase « à confirmer » et faux. Le vrai est `pictures/{instanceId}/{resourceId}`, sans extension. Le refactor uploads est partiellement devancé par `media-storage-plan.md` |
| [plan-import-ia-stats-subsides.md](plan-import-ia-stats-subsides.md) | « Aucune infra email n'existe » → **Resend est en place** (9 templates, domaine vérifié). Quota `AiRequestsPerMonth``AiTokensPerMonth` |
| [security/audit-securite-manager-service.md](security/audit-securite-manager-service.md) | `ImageHelper` : confirmé **code mort** qui planterait sur Linux. Double `SaveChangesAsync` : à revérifier après le fix `BuildAuditEntries` du 2026-08-06 |
| [solution-overview.md](solution-overview.md) | Grille tarifaire **Starter/Standard/Premium (69/99/199 €)** — structure abandonnée. Marquée obsolète |
### 🔴 Demande ton arbitrage — non corrigé volontairement
**[cgu-myinfomate.md](cgu-myinfomate.md) §5 décrit des plans qui n'existent plus** : Starter / Standard / Premium (69 / 99 / 199 € HTVA), quotas IA en requêtes. La réalité est Essentiel / Pro / Premium / Enterprise (39 / 99 / 179 €), quotas en tokens.
C'est un **document contractuel** — je ne l'ai pas réécrit. À reprendre en vérifiant d'abord ce que les clients existants ont effectivement signé.
### Ménage — décidé le 2026-08-07
- **`offre-commerciale.md` : ne pas supprimer.** Vérification faite, il ne contient pas qu'une grille : clients cibles, services complémentaires, forfait événementiel, frais de mise en place, projection de revenus. **Retirer les seules tables de prix** et garder le reste comme document de stratégie commerciale.
- Quatre documents portaient une grille tarifaire (roadmap, solution-overview, offre-commerciale, cgu) alors que la source de vérité est `myinfomate-landing`**supprimer les tables, garder un lien**.
-**CGU §5 reprises le 2026-08-07** depuis la landing : Essentiel/Pro/Premium/Enterprise (39/99/179/devis), et un §5.1bis qui définit le quota IA **en tokens** en renvoyant au back-office pour les valeurs — plutôt que de figer un chiffre dans un contrat.
-**Landing corrigée** — la clé `features.reqPerMonth` n'existe plus dans `myinfomate-landing/src` (vérifié le 2026-08-11). Ce point était encore listé comme ouvert à deux endroits de ce fichier alors que le kanban le donnait fait : c'est le kanban qui avait raison.
- ⚠️ **Champs morts — partiellement traités.** `FactContent`, `IsStepLocked`, `IsHiddenInitially` sont bien supprimés (migration `RemoveDeadGuidedStepFlags`). **Restent en base** : `SectionEvent.ParcoursIds`, `SectionEvent.IconResourceId`, `SectionMap.MapResourceId` — ce dernier étant en plus mappé vers `iconResourceId` dans son DTO, ce qui ajoute une confusion de nommage.
- **Champs « morts » — décision révisée le 2026-08-07 après vérification du code.** Deux des trois ne sont pas morts :
- `SectionMap.MapResourceId`**à renommer, pas à supprimer.** Il est branché de bout en bout (`SectionFactory:179/511`, `SectionMap.ToDTO:58`, `MigrationController:469`) et exposé au front sous le nom `iconResourceId` : la colonne porte l'icône de la carte, pas une « ressource carte ». Le renommer en `IconResourceId` supprime la confusion. Reste à décider si le web doit l'honorer (écart W10).
- `SectionEvent.ParcoursIds`**à investiguer avant de trancher.** Intention produit documentée (depuis un SectionEvent mis en avant, retrouver les parcours liés aux jours d'un carnaval). `GuidedPath.SectionEventId` couvre le lien au niveau *événement* ; reste à vérifier qu'il couvre aussi le niveau *jour / bloc de programme*. Ne pas supprimer tant que ce n'est pas établi.
- `SectionEvent.IconResourceId` → sans usage visiteur identifié, seul candidat réel à la suppression.
- **`Section.meterZoneGPS` : à implémenter, pas à retirer.** Le rayon de déclenchement a une vraie raison d'être variable — une salle de musée demande ~10 m, un parcours extérieur comme le Fort en demande 50-100. La constante en dur à 100 m est fausse dans les deux cas.
---
## 7. Documents de référence (pas des todos — pas besoin de "traiter")
- [solution-overview.md](solution-overview.md) — présentation produit core
- [parity-manager-visitapp.md](parity-manager-visitapp.md) — audit de parité manager-app → apps visiteur (2026-08-05) : couverture par section, 17 écarts au niveau champ, état des builds, méthode pour refaire l'audit
- [guided-path-types.md](guided-path-types.md) — guide de référence SectionParcours
- [section-event-map-setup.md](section-event-map-setup.md) — guide setup SectionEvent/SectionMap
- [design-prompts.md](design-prompts.md) — prompts design pour Google Stitch/Claude Design
- [competitor-analysis.md](competitor-analysis.md) — comparatif concurrents
- [cgu-myinfomate.md](cgu-myinfomate.md) — CGU
- ⚠️ [offre-commerciale.md](offre-commerciale.md) — **obsolète**, source de vérité = myinfomate-landing (voir mémoire `project_pricing_plans`)
- ⚠️ [roadmap.md](roadmap.md) — contient aussi une table "Modules existants" + une table de prix en dehors de la section XR (section 5 ci-dessus) : cette table de prix est une **ancienne copie**, potentiellement désynchronisée de myinfomate-landing (source de vérité) — à vérifier avant de s'y fier
- `openwakeword/` — scripts/notebook d'entraînement de modèle wake word (code, pas de la doc à jour manuellement)
- `claude design/` — mockups HTML générés (MyInfoMate Parcours), à consulter visuellement, pas du texte à synchroniser
---
## Comment mettre à jour ce fichier
> 🗂️ **Le kanban en est le reflet.** Source de vérité du contenu : **[kanban/](kanban/)**, versionné à côté de ce fichier — `kanban.html` en est le rendu généré.
> Publié à l'adresse https://claude.ai/code/artifact/4c317904-2e23-45d3-ac14-dcca678ae5d3
>
> ⚠️ **L'artifact appartient au compte propriétaire.** Lecture et republication **fonctionnent** quand la machine est connectée à ce compte (vérifié le 2026-08-11 ; la note « impossible depuis cette machine » datait d'une session où un autre compte était actif). Si l'artifact n'apparaît pas, basculer de compte. **Avant de republier : lire la version publiée et fusionner** — le fichier du repo et la version en ligne peuvent avoir divergé (constaté le 2026-08-11, une colonne entière de 10 cartes existait en ligne sans être dans le fichier). Jamais de `force:true`. D'où le découpage :
>
> | | Qui | Quoi |
> |---|---|---|
> | Contenu | n'importe quelle session | éditer `DOCS/kanban/` — **un chantier = un fichier `.md`** — puis `python3 DOCS/tools/build_kanban.py` |
> | Diffusion | le compte propriétaire | republier `kanban.html` sur l'artifact |
>
> ⚠️ **`DOCS/kanban.html` est généré : ne pas l'éditer à la main.** Les compteurs (colonnes **et** bandeau du haut) sont calculés à partir du nombre de fichiers — il n'y a plus rien à compter ni à vérifier. Ajouter = créer un fichier dans le dossier de la colonne ; déplacer = déplacer le fichier ; clore = déplacer vers `kanban/done/`. Détail : [kanban/README.md](kanban/README.md).
>
> Un hook `PostToolUse` (`.claude/hooks/status-kanban-reminder.py`) rappelle ce report à chaque écriture sur ce fichier. Il rappelle, il ne publie pas — une correction de formulation ne justifie pas de toucher au tableau.
Ce fichier est un index, pas une source de vérité duplicée. **Les sections lourdes vivent dans [status/](status/)** — une section par fichier ; ici ne restent que leur chapeau et le lien. Éditer le détail dans `status/`, et ne toucher au chapeau que si l'état d'ensemble change.
- Nouvelle feature terminée/à faire → éditer **todo-features.md**, pas ici (sauf si ça change le résumé de la section 1)
- Nouveau point sécurité traité → cocher dans **[status/securite-dette-technique.md](status/securite-dette-technique.md)** **et** noter "Déjà corrigé" dans le fichier audit correspondant
- Nouveau chantier roadmap → ajouter une ligne dans la table de **[status/roadmap-canaux.md](status/roadmap-canaux.md)**, créer le plan détaillé séparément si ça dépasse quelques lignes