DOCS/v1-plan.md
Thomas Fransolet a5a8ecdb20 Documentation interne MyInfoMate / Unov
Import initial de la documentation : statut, roadmap, plans V1/V2,
specs verticales (creche, sport), audits securite, plan de test,
analyse concurrentielle et maquettes de design.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-11 11:17:01 +02:00

32 KiB
Raw Blame History

V1 — plan d'implémentation jusqu'à la mise en prod

Établi le 2026-08-10 en croisant STATUS.md (§1 à §6bis), kanban.html (43 cartes) et des vérifications directes dans le code.

Ce fichier ne remplace ni STATUS.md ni le kanban : il donne l'ordre d'exécution et les liens entre chantiers, que ni l'un ni l'autre ne portent — le kanban trie par état, STATUS.md par domaine, et personne ne dit ce qui doit précéder quoi.

Règle héritée du §1ter et maintenue ici : ce qui touche au schéma part avant la bascule, ce qui est additif attend.


0. Ce que la vérification du code corrige dans les docs

Trois écarts relevés avant d'établir l'ordre. Ils déplacent des chantiers entiers.

Ce que disent les docs Ce que dit le code (2026-08-10) Conséquence sur le plan
§4 : « Rien n'est committé depuis le 2026-07-17 », 75 + 40 + 14 + 4 fichiers en working tree Périmé. Les 4 repos ont un commit du 2026-08-09. Reste non committé : manager-service 41 fichiers (= le lot 3 RAG du 10/08), manager-app 2, tablet-app 4, myinfomate-landing 7, visitapp-web 1 commit non poussé Le point reste en tête mais devient une demi-heure, pas une urgence à trois semaines de travail
§1quater : GetReferencedResourceIds() « réclamé par le plan offline, à écrire dans la même passe » que les 13 GetEmbeddableText() Déjà écrit dans les 13 sous-types (Data/SubSection/*.cs) — la passe commune a bien eu lieu. Mais appelé nulle part : le switch de collecte de ConfigurationController.Export est commenté en bloc (:~400-577), 180 lignes mortes Le bug offline nº1, présenté comme « bloquant produit », coûte le branchement d'une méthode existante à la place d'un bloc mort. Il descend en coût, pas en priorité
§1quater et §6bis : « reste à corriger la landing, features.reqPerMonth dit encore req/mois » Corrigé. La clé n'existe plus dans myinfomate-landing/src Un item de moins ; le kanban avait raison, STATUS.md est en retard sur deux sections

À répercuter dans STATUS.md quand ce plan sera exécuté.


1. Les liens qui déterminent l'ordre

C'est la partie qui manque partout ailleurs. Neuf liens réels, dont quatre imposent l'ordre et cinq évitent de travailler deux fois.

Liens bloquants — l'ordre n'est pas négociable

# Lien Pourquoi
L1 Rotation des secrets → création de l'environnement prod (point 16 du §1quinquies) Écrit noir sur blanc au §1quinquies : « ne pas créer la prod avec les clés committées ». Créer la prod d'abord, c'est rotationner deux fois
L2 Gel du schéma → réécriture de MigrationController BuildSection doit écrire dans le schéma final. Tout renommage ou unification de colonne fait après oblige à repasser dans les 13 branches du mapping. Voir §2 lot B
L3 pg_dump → bascule (point 18) et → activation de visit-events-purge Un seul item débloque deux choses : le filet du jour J, et le job de purge des stats volontairement inactif tant que Stats:RetentionDays n'est pas défini
L4 RGPD → mise en service de VisitorQuestionrepoussé en fin de backlog le 2026-08-11 (lot J) Ce lien reste vrai, mais il ne contraint plus l'ordre : rien ne part en prod avant que tout le backlog soit terminé (§1quater de STATUS.md). Le lot J étant le dernier avant la bascule, le volet RGPD est en place avant la première question de visiteur réelle — la journalisation ne tourne d'ici là que sur des bases de développement. La condition tient tant que cette porte tient : si un déploiement anticipé était décidé, le lot J redeviendrait bloquant

Liens d'économie — même code, une seule passe

# Lien Ce qu'on paie deux fois sinon
L5 Backfill médias ⊂ MigrationController (écart e du §1quinquies) Le §1quater le range dans le lot médias, le §1quinquies dit « à faire dans la migration ». Les deux ont raison : il faut un seul calculateur de StoragePath/SizeBytes, appelé par le backfill local et par la migration. Deux implémentations divergeraient sur les 7 types URL et la ligne sans blob
L6 Rename MapResourceIdIconResourceId = écart (g) du §1quinquies Le §6bis le classe en « nettoyage backend », le §1quinquies en écart de migration. C'est le même changement. Le faire pendant le lot B et l'écart (g) tombe tout seul
L7 Port PG fermé + app.myinfomate.be + compose prod = un seul fichier Trois cartes distinctes du kanban (une « Urgent », une « Planifié », une dans le §1quinquies) qui touchent le même docker-compose et le même Traefik. Les faire séparément, c'est trois fenêtres de redéploiement
L8 Nettoyage manager_api_new → 5 chantiers manager-app Compression images, exposition du watermark, écran audit log, gestion des users, Guide IA lot 4 : tous dans manager-app, tous privés de flutter analyze comme garde-fou tant que les 14 fichiers modèle orphelins produisent 68 erreurs de bruit
L9 Aperçu de conversation (Guide IA lot 4) ≡ « Mode preview / cible Assistant-Persona » de todo-features Deux specs pour le même écran, dans deux documents. Converger avant de coder, comme le note déjà le §1quater

Liens de dépendance fonctionnelle

# Lien Effet
L10 Backfill SizeBytes → quota de stockage autoritaire Sans backfill, SizeBytes = 0 partout : un quota « autoritaire » laisserait tout passer sur l'existant. L'ordre est backfill puis contrôle, jamais l'inverse
L11 §21 (test offline sur device) → priorité des 4 autres bugs offline Le §21 est le seul moyen de savoir si « ça marche mal dans le fort » vient de là. Il conditionne l'urgence, pas l'existence, des bugs de fraîcheur / extensions / purge / échecs silencieux
L12 Déploiement app.myinfomate.be → test §18 (onboarding) L'onboarding vend un plan Essentiel web-only. Jouer le parcours d'inscription de bout en bout sans le web déployé valide tout sauf ce que le client achète
L13 Rétention 13 mois (livrée) → GetSummary en mémoire La fenêtre est passée de 30 jours à 13 mois : eventsQuery.ToList() charge maintenant 13 mois d'événements avant d'agréger. C'est la rétention livrée qui a rendu ce point pressant, pas un nouveau besoin
L14 Bascule multi-instances → post-filtrage HNSW non éprouvé Le RAG a tourné sur une base à une seule instance — exactement le cas où le problème de post-filtrage ne se voit pas. La bascule crée le cas de test en prod. À couvrir avant, par Testcontainers ou à défaut à la main sur deux instances locales
L15 Stats de l'assistant IA ⊂ écran Guide IA « Ce que demandent vos visiteurs » est le deuxième onglet de l'écran Guide IA (§7 du plan), et cette coquille à onglets n'existe pas encore. Deux chantiers séparés = la coquille construite deux fois
L16 Design de l'onglet → job de regroupement en thèmes Ici le design pilote le backend : la forme d'affichage détermine la forme des données agrégées. Job écrit d'abord = job réécrit
L17 Socle visuel constants.dart → les 3 écrans 11 tailles de police dans lib/Components/, pas d'échelle typo. Socle d'abord : les écrans deviennent de l'assemblage. Socle après : 3 repasses
L18 §5 Aperçu de conversation → arbitrage A/B de SectionParcours Le seul gain réel de l'option B est une colonne d'aperçu visiteur en direct — déjà couverte par le §5 du Guide IA (lui-même à converger avec le Mode preview, L9). Le gain de B tombe, A reste

2. Les lots, dans l'ordre

Neuf lots. Le chemin critique passe par A → B → G → H ; les lots C à F sont parallélisables mais tous obligatoires avant H.

Lot A — Sauver et assainir (½ à 1 jour)

Rien de créatif, tout est un verrou pour la suite.

Quoi Lien
Committer le lot 3 RAG du 10/08 manager-service (41 fichiers), manager-app (2), tablet-app (4), myinfomate-landing (7) ; pousser le commit d'avance de visitapp-web
Rotation des secrets : JWT signing key, clés Gemini/OpenWeather, connection strings, mot de passe MQTT, token Telegram → variables d'environnement, purge de l'historique Git, suppression de RELEASE/ manager-service L1 — verrou du lot H
Nettoyer manager_api_new : supprimer les 14 fichiers modèle orphelins + lib/api/openApiTest.dart manager-app L8 — rend flutter analyze exploitable pour les lots D, E, F
Réparer le build tablet-app (échec Gradle sur flutter_tools/gradle/build.gradle.kts) tablet-app Porte du test-plan §0 — les 5 commandes ne peuvent pas toutes passer sans lui
Sécurité rapide : EnableSensitiveDataLogging coupé en prod, backdoor #if DEBUG retirée, exceptions internes plus exposées manager-service À faire avant que la prod existe, pas après

À ne pas régénérer : manager_api_new s'édite à la main (règle du projet). Le nettoyage supprime des fichiers orphelins, il ne relance pas la génération.

Lot B — Geler le schéma (2 à 3 jours) — le point d'articulation

C'est le lot que ni STATUS.md ni le kanban n'isolent, et c'est lui qui conditionne le coût du lot G. Tant qu'un changement de modèle reste ouvert, réécrire MigrationController revient à le réécrire deux fois (L2).

Quoi Décision préalable Lien
Renommer SectionMap.MapResourceIdIconResourceId (branché de bout en bout : SectionFactory:179/511, SectionMap.ToDTO:58, MigrationController:469) Aucune, déjà tranché au §6bis L6 = écart (g)
Supprimer SectionEvent.ParcoursIds — champ, DTO, migration EF Tranché le 2026-08-11 : supprimer. Investigation : le champ n'est lu ni écrit nulle part (entité + DTO + migrations, zéro contrôleur, zéro service), son commentaire dit // Liens vers GeoPoints spécifiques ce qui contredit son nom, et le lien événement↔parcours existe dans l'autre sens via GuidedPath.SectionEventId, lui bien entretenu (SectionMapController:396/449, SectionController:878). Argument décisif : il est porté par SectionEvent, pas par ProgrammeBlock — il ne peut donc pas exprimer « les parcours de tel jour », le cas carnaval pour lequel on le gardait Nettoyage §1 « À faire »
Supprimer SectionEvent.IconResourceId (seul candidat sans usage visiteur identifié) Aucune §6bis
Unifier QuestionTypesorti du lot B le 2026-08-11 Tranché : on ne touche pas au schéma. Le trio TextLibre/Digicode/ExpectedAnswer n'existe pas dans le code : l'enum a trois valeurs (Simple, MultipleChoice, Puzzle) référencées à un seul endroit côté serveur, et le digicode n'est pas un type mais un comportement dérivé d'une réponse numérique. Reste une propreté front : les comparaisons par entier brut (…?.value == 2 ? 'Puzzle' : …) dans showNewOrUpdateGuidedStep.dart:409 et guided_step_challenge.dart:14déplacé au lot E Ne bloque plus MigrationController
Implémenter Section.meterZoneGPS côté visiteur (la colonne existe, une constante à 100 m la remplace) Aucune, tranché au §6bis Bug M3 du kanban — même passe
Flag watermark sur Instance + exposition dans la config, retrait du if instanceId == "633ee379…" de ResourceController:265 Aucune Point 6 du §1ter

Sortie du lot : dotnet build + dotnet test verts, une migration EF unique regroupant les renommages, et plus aucun changement de modèle en attente.

Lot C — Terminer le lot médias (2 à 3 jours)

L'étape A du §1quater est faite (StoragePath/SizeBytes écrits à Create depuis le 10/08). Reste l'étape C.

# Quoi Note
C1 Extraire le calculateur StoragePath + SizeBytes dans un service appelable L5 — consommé par C2 et par l'écart (e) du lot G
C2 Backfill : StoragePath par UPDATE SQL (chemin déterministe pictures/{instanceId}/{resourceId}), SizeBytes par listing du bucket Firebase 37 lignes sur 45. Les 7 types URL (ImageUrl/VideoUrl/JSONUrl) n'ont pas de blob, la 8ᵉ est un type fichier sans URL — inventaire des orphelins au passage
C3 Quota de stockage autoritaire : pré-vol + contrôle à Create + suppression du blob à Delete L10 — après C2, sinon le contrôle porte sur des zéros
C4 Compression images côté client, 2560 px / JPEG q82 (~12×) manager-app. Indépendant de C2 : ne touche que les nouveaux uploads
C5 Alerte de budget GCP 5 minutes. À faire dès maintenant, pas au jour J

Lot D — Bugs offline (1 à 2 jours, après mesure)

Commencer par le test, pas par le code (L11).

Ordre Quoi
D0 Jouer le test-plan §21 sur un device — 15 cas. Sortie : lesquels des 4 bugs restants se manifestent réellement
D1 Remplacer le bloc commenté de ConfigurationController.Export (:~400-577) par des appels à GetReferencedResourceIds() — la méthode existe déjà sur les 13 sous-types. Même geste côté mymuseum-visitapp, où le switch est aussi commenté
D2 Filtre incrémental sur la version de la ressource, pas sa présence
D3 audio/mpeg ajouté à la table d'extensions (audio/mp3 n'est pas un MIME standard)
D4 Réactiver la purge des fichiers obsolètes (deleteSync() commenté)
D5 Échecs de téléchargement remontés au lieu d'un print — une visite incomplète ne doit pas s'annoncer téléchargée

Lot D-bis — Les trois chantiers de design (1,5 à 2 semaines)

État mesuré le 2026-08-10, plus ouvert que ce qu'annoncent STATUS.md et le kanban.

Écran Docs Code
Guide IA « onglet Configuration livré le 07/08 » guide_ia_screen.dart : 545 l., 5 cards en colonne unique, aucun TabBar. Manquent le §4 (Sources de connaissance + recherche vectorielle) et le §5 (Aperçu de conversation)
Stats assistant IA « onglet Vocal (V1) » spécifié statistics_screen.dart : 1481 l., « vocal » n'apparaît que dans un commentaire. Stats assistant à zéro
SectionParcours 2 options maquettées Les 3 popups intactes. Rien n'a commencé

Le problème de méthode avant le contenu. Les trois maquettes sont des artifacts hébergés sur un autre compte Claude : illisibles depuis la machine de dev (constaté le 2026-08-10, cf. le §« Comment mettre à jour ce fichier » de STATUS.md). Toute session qui reprend un de ces écrans redessine — c'est la cause directe du Guide IA livré à la moitié de sa spec.

Même découpage que le kanban : contenu versionné dans le repo, diffusion par artifact depuis le compte propriétaire. La convention existe déjà (DOCS/claude design/, 3 maquettes HTML). Les plans texte, eux, sont précis et suffisent à régénérer les maquettes.

Ordre Quoi Lien
DB0 2026-08-11 HTML des maquettes rapatrié dans DOCS/claude design/guide-ia-screen.html (31,8 Ko), statistics-screen.html (27,4 Ko), sectionparcours-refonte-flux.html (45,6 Ko), JS interactif compris. Méthode : bascule temporaire sur le compte propriétaire, lecture de la source, copie. L'assistant visitapp-web n'a jamais été publié ; le kanban vit déjà dans DOCS/kanban.html La source de vérité du design vit désormais dans le repo, l'artifact ne sert plus qu'à la diffusion. Constat : Guide IA et Statistiques déclarent des tokens rigoureusement identiques — c'était déjà un design system, jamais porté en Dart. Et --brand #264863 est kPrimaryColor
DB1 2026-08-11 Socle visuel dans constants.dart : 11 rôles typographiques, 8 pas d'espacement, 5 rayons, paddings de carte et de page. flutter analyze propre. Décision du 2026-08-11 : l'échelle vient des maquettes, les couleurs restent celles de l'app — un écran neuf se fond dans manager-app, il n'y ouvre pas une seconde palette. Seules les valeurs sans équivalent existant sont reprises (kSurface2/3, kLine, kLineSoft, kInk3, kWarning). Pas de thème sombre. Reste : size.width * 0.2 de NumberInputContainer L17 — sert aussi les 12 autres types de section. La reprise des écrans existants vers ces tokens est un chantier distinct (« repasse UI globale »), pas un effet de bord de celui-ci
DB2 SectionParcours — sauvegarde au fil de l'eau, sans attendre l'arbitrage A/B Indépendante du design, et seul point des trois qui fait perdre du travail au client aujourd'hui : fermer la popup Parcours jette les questions saisies dessous, sans avertir
DB3 Écran Guide IA complet, 2 onglets, stats assistant comprises. 🔨 Front livré le 2026-08-11 : coquille à onglets, §5 Aperçu de conversation branché sur le vrai POST /api/AI/chat (AIApi était dans le client généré mais jamais exposé dans client.dart — câblé), onglet « Ce que demandent vos visiteurs » avec ses tuiles, son bloc ambre des questions sans réponse et ses trois cartes de barres, 24 clés i18n FR/EN/NL. flutter analyze propre, flutter build web .
Complété le 2026-08-11 : carte §4 « Ce que connaît votre guide » sur GET /api/Ai/knowledge/{id} (agrégats ContentEmbedding), journalisation VisitorQuestion dans AiController.Chat, et GET /api/Ai/insights/{id} qui remplit l'onglet. dotnet test 130/130, flutter build web .
Reste dans ce lot : l'onglet « Vocal » des stats et la vérification à l'œil (DB5). Le job de thèmes et tout le volet RGPD sont déplacés au lot J, en fin de backlog. La purge à 90 jours est livrée (VisitorQuestionPurgeService)
L15, L16 — le contrat de données est fixé par GuideIaInsights dans visitor_questions_tab.dart, et GuideInsightsDTO en est le miroir exact : c'est lui que le job de thèmes doit remplir, pas l'inverse. L9 : le §5 converge avec le Mode preview
DB4 SectionParcours — la forme, après arbitrage. Extraire d'abord ParcoursFields / EtapeFields / QuestionFields en widgets autonomes : sert A et B à l'identique L18 — recommandation : A
DB5 Boucle de vérification, à chaque écran : flutter run -d chrome, capture, comparaison côte à côte avec la maquette flutter analyze ne prouve rien sur du design — et reste inexploitable tant que le lot A n'a pas nettoyé manager_api_new (L8)

Ne change pas : la popup de traduction (un niveau justifié, langues verticales, pas d'onglets), et la conception des composants de Components/ — leur problème est le dimensionnement, pas la structure.

Lot E — Parité et finitions visiteur (1 à 2 jours)

Quoi Réf.
Fournisseur de carte honoré en web (LeafletMap.tsx, TileLayer en dur) W1
PDF en média d'étape : liste de types paramétrable côté étape, cas PDF dans getElementForResource et dans ResourceViewer.tsx Les deux derniers points ne sont pas optionnels — sans eux le PDF s'affiche en image cassée, le bug corrigé pour la vidéo le 09/08
Refonte du flux SectionParcours Déplacé au lot D-bis (DB2 et DB4) — c'est un chantier de design, pas une finition de parité

Lot F — Guide IA lot 4 et dette produit (1 semaine)

Quoi Lien
Volet RGPD Déplacé au lot J, fin de backlog (décidé le 2026-08-11)
Insertion VisitorQuestion · purge 90 j · onglet « Ce que demandent vos visiteurs » Livrés le 2026-08-11
Job de regroupement en thèmes Déplacé au lot J (J2) — il conditionne la promesse d'agrégats des CGU
Aperçu de conversation L9 — converger avec « Mode preview / cible Assistant-Persona »
Onglet « Vocal » dans les stats
GetSummary agrégé en SQL au lieu de eventsQuery.ToList() L13 — devenu pressant par la rétention 13 mois
Écran d'audit log (backend fait, front absent) + gestion des users par instance (plafond de 5, compteur UI) L8
Restes non chiffrés : ratio jetons → questions (/1000) calé sur du réel, endpoint de mise à jour d'ApplicationInstance pour rendre les canaux activables, quotas seed 5M/20M ajustés §1quater
Rate limiting sur les API Keys, Stripe Customer Portal §1 « À faire »
Tests du vector store via Testcontainers.PostgreSql sur Dockerfile.postgres L14 — au minimum, éprouver le post-filtrage HNSW à deux instances avant la bascule

Lot G — MigrationController à niveau + dry run (3 à 4 jours) — le plus gros

Les 8 écarts a→h du §1quinquies. Ne démarre qu'après les lots B et C1 (L2, L5).

Écart Quoi Attention
c Instance : 4 champs migrés sur ~35 Le plus dangereux : aucune erreur levée. PublicApiKey null = les apps visiteur ne s'authentifient plus ; WebSlug null = visitapp-web injoignable ; SubscriptionPlanId null = quotas à 0, donc AiTokensPerMonth = 0, donc rien ne s'indexe et l'IA tourne sans compteur. Réutiliser ApplyPlanQuotas
a BuildSection ne couvre que 11 types : SectionEvent et SectionParcours tombent dans le default Section non migrée, silencieusement
b Collections filles jamais remplies (QuizQuestions = new(), EventAgendas = new(), aucun GuidedPath/GuidedStep) Un quiz arrive sans ses questions
d Role = UserRole.ContentEditor en dur Plus aucun administrateur après la bascule
e Colonnes Resource non renseignées, SizeBytes déduit d'un HEAD avalé L5 — appeler le calculateur C1
f IsQRCode/IsSearchText/IsSearchNumber forcés à false Réglages de configuration perdus
g MapResourceId = dto?.iconResourceId L6 — tombe avec le lot B
h Pas de transaction globale, catch qui renvoie 200 OK avec FatalError dans le corps Transaction + vrai code d'erreur. Les tests AnyAsync d'idempotence, eux, sont corrects

Puis : dry run (dryRun=true) sur l'export Mongo de manager-service/migration-data/, et comparaison des comptages par entité avec Mongo. C'est la seule preuve que les écarts a, b et c sont fermés.

Lot H — Tests end-to-end (1 semaine)

Ordre du §2 de STATUS.md, corrigé par L12.

Ordre Quoi Note
H1 §0 — porte d'entrée build, 5 commandes Possible seulement après le lot A (tablet-app)
H2 §8bis / §8ter / §8quater — écran Statistiques, export PDF, rétention Jamais ouvert dans un navigateur. Sur le PDF : accents, et logo — s'il manque, c'est le CORS du bucket Firebase, le rapport doit se générer quand même
H3 §19 — parité par type de section, puis §19.13 cas E (chasse au trésor / carnaval) Le cas E valide SectionMap + SectionParcours + géodéclenchement + questions + mode jeu d'un coup. Le cas 0 valide les 4 correctifs du 09/08
H4 §18 — onboarding self-service L12 — après le déploiement web du lot I. Prérequis de config : alias onboarding@myinfomate.be et whsec_ de la Stripe CLI
H5 §1-17 par lot, §16 non-régression

Lot J — RGPD & conformité (2 à 3 jours) — juste avant le lot I

Placé en fin de backlog le 2026-08-11, à la demande. C'est tenable pour une seule raison : la porte « rien en prod avant la fin du backlog ». La journalisation des questions ne tourne d'ici là que sur des bases de développement, donc aucun visiteur réel n'est concerné. Si cette porte saute, ce lot remonte devant.

Les CGU §8 ont été réécrites le 2026-08-11 (cgu-myinfomate.md) et le texte d'information visiteurs existe (mention-information-visiteurs.md). Ce qui reste :

# Quoi Note
J1 Table d'agrégats de thèmes Les CGU promettent que les regroupements survivent à la purge. Or ThemeId est une colonne de VisitorQuestion : la purge du 90ᵉ jour l'emporte avec la ligne. Sans cette table, la promesse est vide et le client perd son historique au 91ᵉ jour
J2 Job de regroupement en thèmes Sans lui ThemeId reste nul, donc J1 ne contiendrait rien. Il remplit topics dans GuideInsightsDTO — forme déjà arrêtée par l'écran
J3 Interrupteur de collecte par instance ⚠️ Le client est responsable de traitement mais n'a aujourd'hui aucun moyen de refuser la collecte : elle est inconditionnelle dès que l'assistant est actif. Drapeau sur Instance + garde dans RecordVisitorQuestion
J4 Afficher la mention aux visiteurs Le texte existe, il n'est branché ni dans visitapp-web ni dans mymuseum-visitapp. Un lien depuis l'assistant suffit
J5 Faire relire le §8 des CGU Document contractuel, rédigé côté produit et non validé juridiquement. Priorité au §8.6 (sous-traitants) et au §8.5 (transfert hors UE)
J6 Vérifier les conditions réelles de Google Le §8.5 dit « peut impliquer un transfert hors UE » — prudent mais vague. Savoir si l'API Gemini utilisée offre une résidence européenne, et si un DPA est signé. La réponse réécrit le §8.5
J7 Décider si un DPA séparé est requis L'article 28 exige un acte écrit. Le §8 en tient partiellement lieu ; une commune ou un musée subsidié en demandera un en annexe

Un point qui n'est pas un problème : aucune demande d'effacement individuelle n'est exécutable, puisque rien n'est rattachable à une personne. C'est acceptable — la protection tient à l'absence d'identifiant plus la purge, pas à une procédure de droits qui ne pourrait pas s'appliquer.

Lot I — Infra prod et bascule (2 à 3 jours + la fenêtre)

# Quoi Lien
I1 pg_dump quotidien en cron, en complément du snapshot OVH L3 — puis activer Stats__RetentionDays=395 et vérifier que visit-events-purge ne supprime rien
I2 Backup Mongo automatique en cron (dump + docker cp vers l'hôte) Filet du rollback
I3 Un seul chantier compose/Traefik : Dockerfile.postgres épinglé par digest, port 5432 fermé, entrée app.myinfomate.be + Dockerfile visitapp-web L7 — trois cartes, un fichier. L1 : après la rotation des secrets
I4 dotnet ef database update sur la base vide, puis vérifier postgis et vector présents Une base en retard d'une migration fait planter l'API dès le login — c'est arrivé en local le 09/08
I5 Plan de retour arrière écrit, avant le jour J « Un rollback qu'on improvise à 23 h n'est pas un rollback »
I6 pg_dump avant, puis rejouer instance par instance (instanceId=…), en commençant par la plus petite
I7 Vérifications post-bascule : login manager-app, une app visiteur avec sa clé API, un parcours, une carte, un PDF, un quiz avec ses questions Les écarts b et c ne se voient qu'ici
I8 Bascule DNS / API, Mongo gardé en lecture seule quelques jours, pg_dump après

Après la prod

Reprendre contact avec Louise Smets — Musée d'Ixelles, une fois la prod stabilisée. Premier temps d'écoute ; rien n'est su de son rôle exact.


3. Chemin critique et parallélisation

A (½-1j) ──► B (2-3j) ──► G (3-4j) ──► H (1 sem.) ──► I (2-3j + fenêtre)
   │           │
   │           └──► C1 ──► C2 ──► C3
   │                        C4, C5 (indépendants)
   ├──► D0 (test device) ──► D1..D5
   ├──► D-bis : DB0 ──► DB1 ──┬──► DB2
   │                          ├──► DB3 ──► F (job de thèmes)
   │                          └──► DB4 (après arbitrage A/B)
   ├──► E
   └──► F

                              J (RGPD) ──► I   ← dernier avant la bascule
  • Chemin critique : A → B → G → H → I. Tout le reste s'y greffe.
  • C, D, D-bis, E, F sont parallélisables entre eux, mais C1 doit précéder G (L5) et B doit précéder G (L2).
  • DB3 doit précéder le job de thèmes du lot F (L16) : c'est le seul endroit du plan où le design commande le backend.
  • I3 dépend de A (rotation des secrets, L1) et H4 dépend de I3 (L12) : c'est la seule boucle du graphe. Elle se résout en déployant app.myinfomate.be avant la fin de H, ce qui est possible dès que A est fini — le web ne dépend pas de la bascule Postgres.

Ordre de grandeur : 5 à 6 semaines, dont ~1 semaine de tests, 3-4 jours sur MigrationController seul, 1,5 à 2 semaines sur le lot design et 2-3 jours sur le lot J (RGPD). Les deux arbitrages du lot B (ParcoursIds, QuestionType) et celui du lot D-bis (flux SectionParcours) sont les seuls points où le plan attend une décision plutôt qu'un développement.


4. Arbitrages en attente

Sujet Options Bloque
SectionEvent.ParcoursIds 2026-08-11 — supprimer. Champ mort, et structurellement incapable du cas jour/bloc
QuestionType 2026-08-11 — ne pas toucher au schéma. Sort du lot B ; reste une propreté front au lot E
Flux de configuration SectionParcours A — fenêtre unique avec rail d'étapes (recommandée) · B — écran plein cadre 3 colonnes. Argument ajouté le 10/08 : le seul gain réel de B est une colonne d'aperçu visiteur, déjà couverte par le §5 du Guide IA (L18) DB4 seulement — pas DB2, la sauvegarde au fil de l'eau part sans attendre
CGU §5 face aux contrats déjà signés Vérifier ce que les clients existants ont signé avant de considérer le sujet clos Lot F (volet RGPD dans le même document)

4bis. Reprendre ce plan dans une conversation neuve

Ordre de lecture — trois fichiers suffisent, dans cet ordre :

  1. Ce fichier — les 9 lots, les 18 liens, le chemin critique. C'est le seul document qui dit dans quel ordre.
  2. STATUS.md §1sexies — le même découpage, relié au reste de l'état du projet.
  3. kanban.html — où en est chaque chantier, carte par carte.

Les plans détaillés de v2/ ne se lisent qu'au moment d'attaquer le lot concerné.

Amorce à copier telle quelle :

« Lis DOCS/v1-plan.md en entier, puis DOCS/STATUS.md §1sexies. Le plan découpe la V1 en 9 lots (A à I plus D-bis) avec leurs dépendances ; le chemin critique est A → B → G → H → I. Dis-moi où en est chaque lot d'après les documents, puis attaque le lot que je te désigne. Ne relance jamais la génération de manager_api_new : il s'édite à la main. Vérifie l'état réel dans le code avant de croire un document — plusieurs points de STATUS.md se sont révélés périmés. »

Trois pièges vérifiés en vrai, à ne pas re-découvrir :

  • flutter analyze global est inexploitable (68 erreurs de fichiers modèle orphelins, hors graphe de compilation). Filtrer sur le dossier travaillé. Seuls flutter build web, dotnet build, dotnet test disent la vérité.
  • La base locale peut avoir une migration de retard, ce qui fait planter l'API dès le login sans que le compilateur ne dise rien.
  • Le fichier du kanban et la version publiée peuvent avoir divergé — toujours WebFetch l'artifact et fusionner avant de republier, jamais force.

⚠️ Prérequis absolu avant de travailler depuis une autre machine ou un autre poste : committer. Relevé le 2026-08-11 — manager-service a 44 fichiers modifiés dont 9 non suivis (IngestionService, SectionIndexingInterceptor, IVectorStoreService, les DTO du guide…), manager-app 12 dont visitor_questions_tab.dart. Ces fichiers-là n'existent nulle part ailleurs et disparaissent au premier git checkout. C'est le lot A, étape 1.


5. Ce qui reste hors V1 — ne pas y toucher

Reporté en V2 et confirmé ici : SectionForm, ressource 360°, AR image tracking (Mind AR), kiosk web, ingestion documentaire + OCR, TTS pré-généré, guides multiples et thématiques, talking head, génération de visites, envoi mensuel automatique du rapport de stats, XR (Ray-Ban / Quest), dashboard super-admin V2, facturation V2.

Le POC Ray-Ban existe et est démontrable (branche Meta-Rayban-Test, jamais mergée) — c'est un actif commercial, pas un chantier V1. Ne pas vendre l'add-on avant la bascule TTS ElevenLabs → Gemini.