Lot K livré côté tablet-app, et les pièges de vérification de la journée

v1-plan.md — le tableau des crans d'outillage passe de six à dix : le compte de
six datait du moment où la chaîne Gradle passait, avant que la compilation Kotlin
ne révèle la suite. K2 et K4 marqués livrés, avec les deux bugs latents trouvés
pendant le portage et la décision de rendu de l'écran Game (couleurs dérivées de
la configuration, faute de flavor sur tablet-app).

STATUS.md — §1bis passe tablet-app en  ; le diagnostic « un seul défaut, purement
Gradle » y disait le faux depuis le 6 août. §1sexies porte le détail du lot K.

Trois pièges de vérification consignés, tous rencontrés aujourd'hui :
- `flutter build apk … | tail` renvoie le code de sortie du tail, pas celui du
  build : un échec y ressort en « exit code 0 ».
- `flutter analyze` écrit « error - », pas « error • » : un grep sur la mauvaise
  forme a fait conclure à 0 erreur alors qu'il en restait 10.
- un build jamais lancé ne vaut pas un build vert supposé. « À 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.

Réflexe noté pour K5 : trois des quatre derniers crans étaient de simples
alignements sur mymuseum-visitapp. Commencer par un diff des gradle.properties et
build.gradle des deux repos plutôt que de remonter les erreurs une par une.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Thomas Fransolet 2026-08-12 12:18:45 +02:00
parent ff80d9e6d2
commit 059ba3ac2c
3 changed files with 28 additions and 18 deletions

View File

@ -32,7 +32,7 @@ Résumé de la table de suivi de ce fichier :
| 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** | | 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` (**revérifié le 2026-08-11**) | Était ✅ le 2026-08-06, débloqué par la même correction + typage explicite dans `guided_step_challenge.dart:83`. **Casse maintenant côté Gradle**, pas côté Dart : `ndkVersion = "28.2.13676358"` réclamé dans `android/app/build.gradle`, avertissements KGP sur 11 plugins. Côté Dart il reste 4 erreurs, **toutes sur la branche Ray-Ban** (`kElevenLabsApiKey`/`kElevenLabsVoiceId` absents de `constants.dart`, jamais committés — voir §5bis), dans des fichiers hors du graphe de `main.dart`. Même famille que tablet-app : l'environnement Android a bougé | | mymuseum-visitapp | ❌ `flutter build apk` (**revérifié le 2026-08-11**) | Était ✅ le 2026-08-06, débloqué par la même correction + typage explicite dans `guided_step_challenge.dart:83`. **Casse maintenant côté Gradle**, pas côté Dart : `ndkVersion = "28.2.13676358"` réclamé dans `android/app/build.gradle`, avertissements KGP sur 11 plugins. Côté Dart il reste 4 erreurs, **toutes sur la branche Ray-Ban** (`kElevenLabsApiKey`/`kElevenLabsVoiceId` absents de `constants.dart`, jamais committés — voir §5bis), dans des fichiers hors du graphe de `main.dart`. Même famille que tablet-app : l'environnement Android a bougé |
| 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`** | | 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`**mesuré pour de vrai le 2026-08-12** | ⛔ **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é : **six crans d'outillage** (Gradle 7.5 → 8.11.1, AGP 7.2.0 → 8.9.1, Kotlin 1.9.0 → 2.0.10, option AGP supprimée, jcenter mort) — **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)** | | 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é : **six crans d'outillage** (Gradle 7.5 → 8.11.1, AGP 7.2.0 → 8.9.1, Kotlin 1.9.0 → 2.0.10, option AGP supprimée, jcenter mort) — **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** : `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.
@ -326,6 +326,12 @@ Les lots 1 et 2 sont terminés. **Le lot 3 et le lot médias sont tous les deux
**Ce que ça coûte** : +3 à 5 jours. Ce n'est pas du périmètre ajouté, c'est du travail déjà dû que personne n'avait vu. **Ce que ça coûte** : +3 à 5 jours. Ce n'est pas du périmètre ajouté, c'est du travail déjà dû que personne n'avait vu.
**`tablet-app` construit à nouveau — `flutter build apk --debug` vert le 2026-08-12.** K1, K2 et K4 sont livrés et **prouvés par un build**, pas seulement par `flutter analyze`. L'outillage a demandé **dix crans** (le compte de « six » écrit plus tôt dans la journée datait du moment où la chaîne Gradle passait, avant que la compilation Kotlin ne révèle la suite) — tableau complet dans [v1-plan.md lot K](v1-plan.md).
⚠️ **Trois des quatre derniers crans étaient de simples alignements sur `mymuseum-visitapp`** : `-Xmx4096M` au lieu de 1536M, `enableJetifier=false`, et surtout **le retrait d'un `resolutionStrategy` qui forçait `androidx.lifecycle` à 2.4.0** avec le commentaire « To fix mapbox issue » — il réglait un problème mapbox d'il y a trois ans et **causait** celui d'aujourd'hui (`mapbox_maps_flutter` 2.21.1 appelle `setViewTreeLifecycleOwner`, absent de 2.4.0). J'ai cherché ces causes une par une alors qu'un `diff` des deux `gradle.properties` et des deux `build.gradle` les donnait toutes d'un coup. **Réflexe pour K5 : commencer par ce diff.**
**✅ K4 livré le 2026-08-12** — les 6 fichiers de `mymuseum-visitapp/lib/Screens/Sections/Game/` repris dans `tablet-app/lib/Screens/Game/`, `Screens/Puzzle/` supprimé, `main_view` sur `SectionType.Game`. La tablette gagne le **puzzle glissant**, le bouton d'indice et un dimensionnement au ratio de l'image. ⚠️ **Un choix de rendu à valider à l'œil** : mymuseum code ses couleurs en dur par flavor client (rouge MDLF, bleu Fort) ; `tablet-app` n'ayant pas de flavor — un seul APK, la charte vient de la configuration — le dégradé est **dérivé de `configuration.primaryColor`**. Le rendu diffère donc de la version mobile.
**✅ K2 livré le 2026-08-12 — les 132 erreurs de contrat sont tombées.** `flutter analyze lib` ne renvoie plus que 6 erreurs `PuzzleDTO`, qui relèvent de K4. Livré : `TabletAppContext.currentAppConfigurationLink`, `lib/Helpers/geo.dart` (extension `lat`/`lng` + `coordinateKey`), 67 accès `roundedValue` rebranchés, `main_view` et les 4 vues carte portées. **✅ K2 livré le 2026-08-12 — les 132 erreurs de contrat sont tombées.** `flutter analyze lib` ne renvoie plus que 6 erreurs `PuzzleDTO`, qui relèvent de K4. Livré : `TabletAppContext.currentAppConfigurationLink`, `lib/Helpers/geo.dart` (extension `lat`/`lng` + `coordinateKey`), 67 accès `roundedValue` rebranchés, `main_view` et les 4 vues carte portées.
**Deux bugs latents trouvés au passage — la raison pour laquelle un chercher-remplacer aurait été dangereux :** **Deux bugs latents trouvés au passage — la raison pour laquelle un chercher-remplacer aurait été dangereux :**

View File

@ -454,13 +454,13 @@
</header> </header>
<section class="summary" aria-label="Chiffres clés"> <section class="summary" aria-label="Chiffres clés">
<div class="stat"><span class="n n-critical">4</span><span class="k">Urgent</span></div> <div class="stat"><span class="n n-critical">3</span><span class="k">Urgent</span></div>
<div class="stat"><span class="n n-info">2</span><span class="k">Migration v3</span></div> <div class="stat"><span class="n n-info">2</span><span class="k">Migration v3</span></div>
<div class="stat"><span class="n n-warn">5</span><span class="k">Bugs ouverts</span></div> <div class="stat"><span class="n n-warn">5</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">6</span><span class="k">À tester</span></div>
<div class="stat"><span class="n">20</span><span class="k">Planifié</span></div> <div class="stat"><span class="n">20</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-gate">8</span><span class="k">Bascule prod</span></div>
<div class="stat"><span class="n n-good">38</span><span class="k">Fait récemment</span></div> <div class="stat"><span class="n n-good">39</span><span class="k">Fait récemment</span></div>
</section> </section>
<div class="filters" role="group" aria-label="Filtrer par domaine"> <div class="filters" role="group" aria-label="Filtrer par domaine">
@ -486,7 +486,7 @@
<!-- URGENT --> <!-- URGENT -->
<section class="col" style="--stripe: var(--critical)"> <section class="col" style="--stripe: var(--critical)">
<div class="col-head"><h2>Urgent</h2><span class="count">4</span></div> <div class="col-head"><h2>Urgent</h2><span class="count">3</span></div>
<div class="stack"> <div class="stack">
<article class="card" data-area="infra"> <article class="card" data-area="infra">
@ -505,16 +505,6 @@
<span class="src">STATUS.md §1bis · §5bis</span> <span class="src">STATUS.md §1bis · §5bis</span>
</article> </article>
<article class="card" data-area="manager" data-horizon="v1">
<div class="card-meta"><span class="tag">tablet-app</span><span class="flag f-critical">Lot K — bloque la bascule</span></div>
<h3>tablet-app — reste le renommage Puzzle → Game (K4)</h3>
<p><strong>Le premier vrai build, le 12/08, a fait sauter le diagnostic de cette carte.</strong> « Un seul défaut, purement Gradle, peut-être réglé par <code>5a6701d</code> » était doublement faux — il tenait parce que personne n'avait lancé la commande. L'outillage Android demandait <strong>six crans</strong> (Gradle 7.5 → 8.11.1, AGP 7.2.0 → 8.9.1, Kotlin 2.0.10, jcenter mort) : <strong>fait le 12/08</strong>. Derrière, le Gradle masquait <strong>132 erreurs Dart</strong><strong>portage K2 livré le 12/08</strong>, elles sont toutes tombées.</p>
<p><strong>Il ne reste que 6 erreurs</strong>, toutes <code>PuzzleDTO</code> / <code>SectionType.Puzzle</code> : c'est <strong>K4</strong>, à traiter en reprenant <code>mymuseum-visitapp/lib/Screens/Sections/Game/</code>. Voir la carte « Types de section manquants sur le kiosk ».</p>
<p>⚠️ <strong>Pourquoi ça bloque la bascule (L19)</strong> : les tablettes en service chez MDLF et au Fort parlent l'ancien contrat. Tant qu'un APK à jour n'est pas publié, le jour J elles ne s'arrêteraient pas proprement — cartes sans points, écrans dégradés, aucune erreur visible.</p>
<p>⚠️ <strong>Piège vérifié le 12/08 : deux conventions de coordonnées coexistent.</strong> Les <code>GeoPointDTO</code> sont en <code>[lng, lat]</code> (GeoJSON, écrit par <code>GeometryMapper</code>), les géométries de <code>GuidedStep</code> en <code>[lat, lng]</code> (écrit par manager-app). Les deux sont en prod, et <strong>une confusion ne lève aucune erreur</strong> — elle déplace les points sur une carte qui reste plausible. Ne jamais recopier un helper de coordonnées d'un contexte à l'autre. Détail : STATUS.md §1sexies.</p>
<span class="src">v1-plan.md lot K · STATUS.md §1bis</span>
</article>
<article class="card" data-area="manager" data-horizon="v1"> <article class="card" data-area="manager" data-horizon="v1">
<div class="card-meta"><span class="tag">tablet-app</span><span class="flag f-warn">Lot K · périmètre kiosk</span></div> <div class="card-meta"><span class="tag">tablet-app</span><span class="flag f-warn">Lot K · périmètre kiosk</span></div>
<h3>Types de section manquants sur le kiosk</h3> <h3>Types de section manquants sur le kiosk</h3>
@ -875,7 +865,7 @@
<section class="done"> <section class="done">
<h2>Fait récemment</h2> <h2>Fait récemment</h2>
<p>Trente-huit chantiers clos entre le 5 et le 12 août 2026.</p> <p>Trente-neuf chantiers clos entre le 5 et le 12 août 2026.</p>
<div class="done-grid"> <div class="done-grid">
<div class="done-item"> <div class="done-item">
@ -884,6 +874,11 @@
<span><strong>C3</strong> : ⚠️ le pré-vol <strong>existait déjà à moitié et ce n'était écrit nulle part</strong><code>Upload</code> (multipart) contrôlait, <code>Create</code> (JSON) ne contrôlait rien, or c'est le chemin qu'emprunte manager-app. ⚠️ Et les deux lectures du quota <strong>divergeaient</strong> : <code>Upload</code> lisait le <em>plan</em>, <code>GetQuota</code> l'<em>instance</em> avec le plan en repli — une instance à quota surchargé (le mécanisme de l'add-on) affichait un chiffre et se faisait bloquer sur un autre. Fermé par <code>StorageQuota</code>. <code>Delete</code> supprime le blob <strong>avant</strong> la ligne et renvoie <strong>502</strong> en la conservant si le bucket échoue : une ressource encore listée se rattrape, un blob que plus aucune ligne ne désigne est facturé à l'aveugle. ✅ L'angle mort laissé par C1 était une <strong>fausse crainte</strong> : <code>PathFor</code> ne construit qu'un <code>pictures/{instanceId}/{resourceId}</code>, le type n'entre pas dans le chemin. <strong>Aucun secret nouveau</strong> : <code>FirebaseAdmin</code> était déjà là pour le push, <code>Google.Cloud.Storage.V1</code> réutilise le même credential. <code>dotnet test</code> <strong>163/163</strong>.</span> <span><strong>C3</strong> : ⚠️ le pré-vol <strong>existait déjà à moitié et ce n'était écrit nulle part</strong><code>Upload</code> (multipart) contrôlait, <code>Create</code> (JSON) ne contrôlait rien, or c'est le chemin qu'emprunte manager-app. ⚠️ Et les deux lectures du quota <strong>divergeaient</strong> : <code>Upload</code> lisait le <em>plan</em>, <code>GetQuota</code> l'<em>instance</em> avec le plan en repli — une instance à quota surchargé (le mécanisme de l'add-on) affichait un chiffre et se faisait bloquer sur un autre. Fermé par <code>StorageQuota</code>. <code>Delete</code> supprime le blob <strong>avant</strong> la ligne et renvoie <strong>502</strong> en la conservant si le bucket échoue : une ressource encore listée se rattrape, un blob que plus aucune ligne ne désigne est facturé à l'aveugle. ✅ L'angle mort laissé par C1 était une <strong>fausse crainte</strong> : <code>PathFor</code> ne construit qu'un <code>pictures/{instanceId}/{resourceId}</code>, le type n'entre pas dans le chemin. <strong>Aucun secret nouveau</strong> : <code>FirebaseAdmin</code> était déjà là pour le push, <code>Google.Cloud.Storage.V1</code> réutilise le même credential. <code>dotnet test</code> <strong>163/163</strong>.</span>
</div> </div>
<div class="done-item">
<strong>tablet-app construit à nouveau — APK produit pour la première fois depuis avril</strong>
<span><code>flutter build apk --debug</code> <strong>vert</strong>. K1, K2 et K4 sont livrés et <em>prouvés par un build</em>, pas seulement par <code>flutter analyze</code>. L'outillage a demandé <strong>dix crans</strong>, pas les six annoncés en cours de journée : ce compte datait du moment où la chaîne Gradle passait, avant que la compilation Kotlin ne révèle la suite — heap à 4096M, Jetifier coupé, forçage <code>androidx.lifecycle</code> retiré, Kotlin porté à 2.3.10. ⚠️ <strong>Trois de ces quatre derniers crans étaient de simples alignements sur mymuseum-visitapp</strong>, qui utilise le même plugin mapbox sans rencontrer aucun de ces problèmes : les causes ont été cherchées une par une alors qu'un <code>diff</code> des deux <code>gradle.properties</code> les donnait toutes d'un coup. <strong>Réflexe pour K5 : commencer par ce diff.</strong><br><br>Le cran le plus instructif : un <code>resolutionStrategy</code> forçait <code>androidx.lifecycle</code> à 2.4.0 sous le commentaire « To fix mapbox issue ». Il réglait un problème d'il y a trois ans et <strong>causait</strong> celui d'aujourd'hui — <code>mapbox_maps_flutter</code> 2.21.1 appelle <code>setViewTreeLifecycleOwner</code>, absent de 2.4.0. Du contournement fossilisé, comme les trois blocs commentés qui ont fait mentir les diagnostics le même jour. K4 apporte au passage à la tablette le <strong>puzzle glissant</strong>, le bouton d'indice et un dimensionnement au ratio de l'image.</span>
</div>
<div class="done-item"> <div class="done-item">
<strong>tablet-app rebranché sur le contrat d'API de Postgres v3 (K2)</strong> <strong>tablet-app rebranché sur le contrat d'API de Postgres v3 (K2)</strong>
<span>Les <strong>132 erreurs Dart</strong> que le Gradle masquait sont tombées : <code>flutter analyze lib</code> ne renvoie plus que les 6 <code>PuzzleDTO</code> de K4. Livré : <code>TabletAppContext.currentAppConfigurationLink</code> (copie du getter de mymuseum), <code>lib/Helpers/geo.dart</code> avec l'extension <code>lat</code>/<code>lng</code>, 67 accès <code>roundedValue</code> rebranchés, <code>main_view</code> et les 4 vues carte portées. La formule de clé par coordonnées de <code>geo_point_filter</code>, <strong>dupliquée à quatre endroits</strong> alors que les deux côtés de l'appariement doivent produire la même valeur, ne vit plus qu'à un seul.<br><br>⚠️ <strong>Deux bugs latents trouvés — la raison pour laquelle un chercher-remplacer aurait été dangereux.</strong> <strong>(1)</strong> <code>applicationInstanceDTO</code> n'était assigné <em>que si</em> l'instance avait l'IA : il servait de drapeau d'assistant. Ce DTO portant désormais les <code>AppConfigurationLink</code>, le nouveau getter aurait renvoyé <code>null</code> sur <strong>toute instance sans IA — donc MDLF et le Fort</strong> — et les cinq réglages d'affichage seraient retombés sur leurs défauts, <em>sans erreur ni log</em>. Drapeau rendu explicite (<code>isAssistantEnabled</code>), en préservant le ET instance × canal. <strong>(2)</strong> <code>ConfigurationDTO.isTablet</code> ayant disparu, le filtre « configurations pour tablette » n'avait plus de source : il porte maintenant sur les liens du canal <code>AppType.Tablet</code>. <strong>À valider sur device</strong> — une configuration non rattachée au canal tablette rendra l'écran de sélection vide.</span> <span>Les <strong>132 erreurs Dart</strong> que le Gradle masquait sont tombées : <code>flutter analyze lib</code> ne renvoie plus que les 6 <code>PuzzleDTO</code> de K4. Livré : <code>TabletAppContext.currentAppConfigurationLink</code> (copie du getter de mymuseum), <code>lib/Helpers/geo.dart</code> avec l'extension <code>lat</code>/<code>lng</code>, 67 accès <code>roundedValue</code> rebranchés, <code>main_view</code> et les 4 vues carte portées. La formule de clé par coordonnées de <code>geo_point_filter</code>, <strong>dupliquée à quatre endroits</strong> alors que les deux côtés de l'appariement doivent produire la même valeur, ne vit plus qu'à un seul.<br><br>⚠️ <strong>Deux bugs latents trouvés — la raison pour laquelle un chercher-remplacer aurait été dangereux.</strong> <strong>(1)</strong> <code>applicationInstanceDTO</code> n'était assigné <em>que si</em> l'instance avait l'IA : il servait de drapeau d'assistant. Ce DTO portant désormais les <code>AppConfigurationLink</code>, le nouveau getter aurait renvoyé <code>null</code> sur <strong>toute instance sans IA — donc MDLF et le Fort</strong> — et les cinq réglages d'affichage seraient retombés sur leurs défauts, <em>sans erreur ni log</em>. Drapeau rendu explicite (<code>isAssistantEnabled</code>), en préservant le ET instance × canal. <strong>(2)</strong> <code>ConfigurationDTO.isTablet</code> ayant disparu, le filtre « configurations pour tablette » n'avait plus de source : il porte maintenant sur les liens du canal <code>AppType.Tablet</code>. <strong>À valider sur device</strong> — une configuration non rattachée au canal tablette rendra l'écran de sélection vide.</span>

View File

@ -253,7 +253,9 @@ Ordre du §2 de STATUS.md, corrigé par **L12**.
> >
> ⚠️ **Le point qui fait de ce lot un bloquant de la bascule, et que le plan ne portait nulle part : le contrat d'API a changé, les apps déployées parlent l'ancien.** `ConfigurationDTO.roundedValue` et `GeoPointDTO.latitude/longitude` ne sont plus servis par Postgres v3. Une tablette en service chez MDLF ou au Fort ne s'arrêterait pas proprement le jour J — elle afficherait des cartes sans points et des écrans dégradés. Voir **L19**. > ⚠️ **Le point qui fait de ce lot un bloquant de la bascule, et que le plan ne portait nulle part : le contrat d'API a changé, les apps déployées parlent l'ancien.** `ConfigurationDTO.roundedValue` et `GeoPointDTO.latitude/longitude` ne sont plus servis par Postgres v3. Une tablette en service chez MDLF ou au Fort ne s'arrêterait pas proprement le jour J — elle afficherait des cartes sans points et des écrans dégradés. Voir **L19**.
**K1 — ✅ Outillage Android `tablet-app`, fait le 2026-08-12.** Six crans, chacun dicté par l'erreur du précédent — à ne pas re-découvrir un par un : **`tablet-app` construit — `flutter build apk --debug` vert le 2026-08-12**, APK produit pour la première fois depuis le 16 avril. K1, K2 et K4 sont livrés et **prouvés par un build**, pas seulement par `flutter analyze`. L'APK passe de 174 à 248 Mo : effet attendu du cran 3 (bibliothèques natives non compressées).
**K1 — Outillage Android `tablet-app`, 2026-08-12. Dix crans**, chacun dicté par l'erreur du précédent — à ne pas re-découvrir un par un. ⚠️ **Le compte de « six crans » écrit plus tôt dans la journée était provisoire** : il datait du moment où la chaîne Gradle passait, avant que la compilation Kotlin ne révèle la suite.
| # | Changement | Ce qu'il débloquait | | # | Changement | Ce qu'il débloquait |
|---|---|---| |---|---|---|
@ -263,6 +265,12 @@ Ordre du §2 de STATUS.md, corrigé par **L12**.
| 4 | AGP → **8.7.3** | minimum exigé par Flutter (8.6.0) | | 4 | AGP → **8.7.3** | minimum exigé par Flutter (8.6.0) |
| 5 | AGP → **8.9.1** | réclamé par `androidx.browser:1.9.0` et `androidx.core:core-ktx:1.17.0`, tirés par les plugins | | 5 | AGP → **8.9.1** | réclamé par `androidx.browser:1.9.0` et `androidx.core:core-ktx:1.17.0`, tirés par les plugins |
| 6 | `jcenter()``mavenCentral()` | jcenter est arrêté depuis 2021 | | 6 | `jcenter()``mavenCentral()` | jcenter est arrêté depuis 2021 |
| 7 | `jvmargs` **1536M → 4096M** | `Java heap space` dans `JetifyTransform` sur les jars Flutter |
| 8 | `android.enableJetifier` **true → false** | Jetifier ne sert plus à rien depuis qu'AndroidX est partout, et c'est lui qui saturait la heap |
| 9 | **`resolutionStrategy` sur `androidx.lifecycle` retiré** | ⚠️ **Le plus instructif** : il forçait la version **2.4.0** avec le commentaire « To fix mapbox issue ». Il réglait un problème mapbox d'il y a trois ans et **causait** celui d'aujourd'hui — `mapbox_maps_flutter` 2.21.1 appelle `setViewTreeLifecycleOwner`, absent de 2.4.0, et sa compilation Kotlin échouait. Ne pas le remettre : un commentaire le dit dans `android/build.gradle` |
| 10 | Kotlin **2.0.10 → 2.3.10** | les dépendances tirent `kotlin-stdlib 2.3.10`, métadonnées binaires incompatibles avec un compilateur 2.0. Satisfait au passage l'exigence Flutter ≥ 2.2.20, annoncée comme prochaine rupture |
**Les crans 7, 8 et 9 sont des alignements sur `mymuseum-visitapp`**, qui utilise le même plugin mapbox et ne rencontre aucun de ces problèmes : `-Xmx4096M`, `enableJetifier=false`, aucun forçage de `lifecycle`. **Réflexe à garder pour K5** : avant de chercher, comparer les deux `gradle.properties` et les deux `build.gradle`.
⚠️ **Un bloc `buildscript` entièrement commenté dans `android/build.gradle`** déclarait AGP 8.5.0 / Kotlin 2.0.10 alors que la config appliquée vivait dans `settings.gradle` (AGP 7.2.0 / Kotlin 1.9.0). Il a fait conclure deux fois à une configuration qui n'était pas celle du build — **il est supprimé**, les versions ne sont plus déclarées qu'à un seul endroit. C'est le troisième piège « bloc commenté » du projet, après les deux pistes Dart écartées le 11/08. ⚠️ **Un bloc `buildscript` entièrement commenté dans `android/build.gradle`** déclarait AGP 8.5.0 / Kotlin 2.0.10 alors que la config appliquée vivait dans `settings.gradle` (AGP 7.2.0 / Kotlin 1.9.0). Il a fait conclure deux fois à une configuration qui n'était pas celle du build — **il est supprimé**, les versions ne sont plus déclarées qu'à un seul endroit. C'est le troisième piège « bloc commenté » du projet, après les deux pistes Dart écartées le 11/08.
@ -273,7 +281,8 @@ Ordre du §2 de STATUS.md, corrigé par **L12**.
| ~~**K2**~~**2026-08-12** | **Portage livré. Les 132 erreurs de contrat sont tombées** ; `flutter analyze lib` ne renvoie plus que les 6 erreurs `PuzzleDTO`, qui sont K4. Fait : `TabletAppContext.currentAppConfigurationLink` (copie du getter mymuseum), `lib/Helpers/geo.dart` avec l'extension `lat`/`lng`, 67 accès `roundedValue` rebranchés, `main_view` et les 4 vues carte portées.<br>⚠️ **Deux bugs latents trouvés — c'est ce qui justifiait de le faire à la main.**<br>**(1)** `applicationInstanceDTO` n'était assigné **que si `instanceDTO.isAssistant == true`** (`main_view:339`) : il servait de drapeau d'assistant. Ce DTO portant désormais les `AppConfigurationLink`, le getter aurait renvoyé `null` sur **toute instance sans IA — donc MDLF et le Fort** — et les cinq réglages seraient retombés sur leurs défauts, sans erreur ni log. Corrigé : assignation inconditionnelle + drapeau explicite `isAssistantEnabled`, qui **préserve le ET** instance × canal (les deux gardes de `AiController:315/360`).<br>**(2)** `ConfigurationDTO.isTablet` a disparu : le filtre « configurations pour tablette » de `getConfigurations` n'avait plus de source. Il porte désormais sur les `AppConfigurationLink` du canal `AppType.Tablet`, même appel que `main_view`. ⚠️ **À valider sur device (§0/§18)** : si un client a des configurations non rattachées à son canal tablette, l'écran de sélection sera **vide**. L'ancien code avait le même angle mort, mais la donnée qui pilote le filtre a changé.<br>⚠️ **Piège de vérification** : `flutter analyze` écrit `error - `, pas `error • ` — un grep sur la mauvaise forme a fait conclure « 0 erreur » alors qu'il en restait 10 | | ~~**K2**~~**2026-08-12** | **Portage livré. Les 132 erreurs de contrat sont tombées** ; `flutter analyze lib` ne renvoie plus que les 6 erreurs `PuzzleDTO`, qui sont K4. Fait : `TabletAppContext.currentAppConfigurationLink` (copie du getter mymuseum), `lib/Helpers/geo.dart` avec l'extension `lat`/`lng`, 67 accès `roundedValue` rebranchés, `main_view` et les 4 vues carte portées.<br>⚠️ **Deux bugs latents trouvés — c'est ce qui justifiait de le faire à la main.**<br>**(1)** `applicationInstanceDTO` n'était assigné **que si `instanceDTO.isAssistant == true`** (`main_view:339`) : il servait de drapeau d'assistant. Ce DTO portant désormais les `AppConfigurationLink`, le getter aurait renvoyé `null` sur **toute instance sans IA — donc MDLF et le Fort** — et les cinq réglages seraient retombés sur leurs défauts, sans erreur ni log. Corrigé : assignation inconditionnelle + drapeau explicite `isAssistantEnabled`, qui **préserve le ET** instance × canal (les deux gardes de `AiController:315/360`).<br>**(2)** `ConfigurationDTO.isTablet` a disparu : le filtre « configurations pour tablette » de `getConfigurations` n'avait plus de source. Il porte désormais sur les `AppConfigurationLink` du canal `AppType.Tablet`, même appel que `main_view`. ⚠️ **À valider sur device (§0/§18)** : si un client a des configurations non rattachées à son canal tablette, l'écran de sélection sera **vide**. L'ancien code avait le même angle mort, mais la donnée qui pilote le filtre a changé.<br>⚠️ **Piège de vérification** : `flutter analyze` écrit `error - `, pas `error • ` — un grep sur la mauvaise forme a fait conclure « 0 erreur » alors qu'il en restait 10 |
| ~~K2 — notes de conception~~ | ⚠️ **Deux conventions de coordonnées coexistent** dans le projet — `GeoPointDTO` en `[lng, lat]`, géométries de `GuidedStep` en `[lat, lng]`. Une confusion **ne lève aucune erreur**, elle déplace les points. Détail et tableau dans **STATUS.md §1sexies**. Fondations déjà posées le 2026-08-12 : `TabletAppContext.currentAppConfigurationLink` et `lib/Helpers/geo.dart`.<br>**Portage du contrat d'API** — **132 erreurs Dart, 3 causes** : `ConfigurationDTO.roundedValue`/`isDate`/`isHour`/`isSectionImageBackground`/`screenPercentageSectionsMainPage` ont migré vers **`AppConfigurationLinkDTO`** (73 err.) · `GeoPointDTO.latitude/longitude`**`GeometryDTO? geometry`**, effet de PostGIS (40 err.) · les `min()` sur `dynamic` en découlent (19 err.) | **Le modèle est déjà écrit** : `mymuseum-visitapp` a fait ce portage (`visitAppContext.currentAppConfigurationLink?.roundedValue`, `Models/visitContext.dart`). Copier, ne pas concevoir. Les 40 sites `latitude`/`longitude` se traitent par **une extension Dart sur `GeoPointDTO`** plutôt qu'un à un | | ~~K2 — notes de conception~~ | ⚠️ **Deux conventions de coordonnées coexistent** dans le projet — `GeoPointDTO` en `[lng, lat]`, géométries de `GuidedStep` en `[lat, lng]`. Une confusion **ne lève aucune erreur**, elle déplace les points. Détail et tableau dans **STATUS.md §1sexies**. Fondations déjà posées le 2026-08-12 : `TabletAppContext.currentAppConfigurationLink` et `lib/Helpers/geo.dart`.<br>**Portage du contrat d'API** — **132 erreurs Dart, 3 causes** : `ConfigurationDTO.roundedValue`/`isDate`/`isHour`/`isSectionImageBackground`/`screenPercentageSectionsMainPage` ont migré vers **`AppConfigurationLinkDTO`** (73 err.) · `GeoPointDTO.latitude/longitude`**`GeometryDTO? geometry`**, effet de PostGIS (40 err.) · les `min()` sur `dynamic` en découlent (19 err.) | **Le modèle est déjà écrit** : `mymuseum-visitapp` a fait ce portage (`visitAppContext.currentAppConfigurationLink?.roundedValue`, `Models/visitContext.dart`). Copier, ne pas concevoir. Les 40 sites `latitude`/`longitude` se traitent par **une extension Dart sur `GeoPointDTO`** plutôt qu'un à un |
| **K3** | **Périmètre kiosk — tranché le 2026-08-12.** `tablet-app` couvre **11 des 13** types. **`SectionParcours` : exclu définitivement** — un parcours guidé fait marcher le visiteur avec géodéclenchement, ça n'a aucun sens sur borne fixe. **`SectionEvent` : à supporter** — une borne d'accueil qui affiche le programme du jour et le plan est le cas d'usage kiosk par excellence, et c'est le scénario du §19.13 cas E | Ni l'un ni l'autre n'est un retard : les deux types **sont nés avec Postgres v3**, ils n'ont jamais existé dans Mongo (cf. lot G, écart a) | | **K3** | **Périmètre kiosk — tranché le 2026-08-12.** `tablet-app` couvre **11 des 13** types. **`SectionParcours` : exclu définitivement** — un parcours guidé fait marcher le visiteur avec géodéclenchement, ça n'a aucun sens sur borne fixe. **`SectionEvent` : à supporter** — une borne d'accueil qui affiche le programme du jour et le plan est le cas d'usage kiosk par excellence, et c'est le scénario du §19.13 cas E | Ni l'un ni l'autre n'est un retard : les deux types **sont nés avec Postgres v3**, ils n'ont jamais existé dans Mongo (cf. lot G, écart a) |
| **K4** | **`SectionPuzzle``SectionGame` + le type glissant.** Le serveur a renommé et porte `GameTypes { Puzzle, SlidingPuzzle }` ; `tablet-app` a encore un écran `Puzzle/` qui ne connaît que l'ancien. **Reprendre le code de `mymuseum-visitapp/lib/Screens/Sections/Game/`**`game_page.dart` et `sliding_puzzle_piece.dart` existent déjà là-bas, et cette version est **moins buguée** que celle de tablet-app | Récupération, pas réécriture | | ~~**K4**~~**2026-08-12** | **Écran Game repris de mymuseum.** Les 6 fichiers de `Screens/Sections/Game/` sont dans `tablet-app/lib/Screens/Game/`, contexte adapté ; `lib/Screens/Puzzle/` supprimé ; `main_view` bascule sur `SectionType.Game` / `GameDTO` / `GamePage`. **Gains** : le puzzle glissant (mélange par mouvements valides, donc toujours soluble), le bouton d'indice, et un dimensionnement qui respecte le ratio de l'image — 508 lignes contre 237 à l'ancien `puzzle_view`.<br>⚠️ **Une décision de rendu à valider à l'œil** : mymuseum code ses couleurs **en dur par flavor client** (`kMainColor0/1/2` — rouge MDLF, bleu Fort). `tablet-app` n'a pas de flavor : un seul APK sert tous les clients et la charte vient de la configuration choisie au pincode. Recopier ces constantes aurait affiché du bleu chez MDLF. Le dégradé et le bouton d'indice sont donc **dérivés de `configuration.primaryColor`** (même parsing que `audio_player`/`loading_common`, repli `kTestSecondColor`, 3 nuances par `Color.lerp`). Plus juste sur le fond, mais **le rendu diffère de la version mobile** : si le dégradé dessiné à la main est préféré, figer les trois couleurs |
| ~~K4 — énoncé d'origine~~ | **`SectionPuzzle``SectionGame` + le type glissant.** Le serveur a renommé et porte `GameTypes { Puzzle, SlidingPuzzle }` ; `tablet-app` a encore un écran `Puzzle/` qui ne connaît que l'ancien. **Reprendre le code de `mymuseum-visitapp/lib/Screens/Sections/Game/`**`game_page.dart` et `sliding_puzzle_piece.dart` existent déjà là-bas, et cette version est **moins buguée** que celle de tablet-app | Récupération, pas réécriture |
| **K5** | **`mymuseum-visitapp` : même rattrapage d'outillage.** Son APK ne compile pas non plus (Gradle/NDK) — c'est ce qui empêche de vérifier D1 sur device et **bloque D0 / test-plan §21** | Son code est déjà porté sur le nouveau contrat ; c'est l'outillage qui manque, pas le portage | | **K5** | **`mymuseum-visitapp` : même rattrapage d'outillage.** Son APK ne compile pas non plus (Gradle/NDK) — c'est ce qui empêche de vérifier D1 sur device et **bloque D0 / test-plan §21** | Son code est déjà porté sur le nouveau contrat ; c'est l'outillage qui manque, pas le portage |
| **K6** | **Publier les APK à jour**, puis vérifier le renouvellement du parc | **L19**. Les tablettes kiosk sont des appareils que tu gères — mise à jour maîtrisée. **Les téléphones des visiteurs, non** : aucune manœuvre technique ne force une mise à jour, d'où la stratégie de coexistence du lot I | | **K6** | **Publier les APK à jour**, puis vérifier le renouvellement du parc | **L19**. Les tablettes kiosk sont des appareils que tu gères — mise à jour maîtrisée. **Les téléphones des visiteurs, non** : aucune manœuvre technique ne force une mise à jour, d'où la stratégie de coexistence du lot I |