LifePlay-Revival/Docs/Migration/etat/REL/MAPDL1/MAPDL1-REX-2026-10-22.md
2026-10-22 21:10:39 +02:00

158 lines
16 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.

Fiche MAPDL1-REX-2026-10-22 — Niveau N2 — Modèle prévu opus medium | utilisé claude-opus-5-5 — Agent session « Medium · MAPDL1 » — Skills sk-protocol, lp-lot, lp-city
# MAPDL1 : retour d'expérience pour les skills et les agents (2026-10-22)
Demande de l'utilisateur, citée mot pour mot par Big Boss : « je souhaite aussi leur retour d'expérience pour enrichir les skills et agents ».
Cette fiche ne touche **ni les skills ni les agents** : Big Boss les posera après l'accord de l'utilisateur.
Chaque leçon donne quatre choses :
- **Fait** : ce qui a été observé, avec sa preuve (fichier:ligne, ou livrable du lot) ;
- **Règle** : ce qu'il faut retenir ;
- **Où** : le skill et sa section, ou l'agent ;
- **Texte** : un texte prêt à coller.
Priorités :
- **P1** : évite une perte de données, une build cassée ou une donnée fausse ;
- **P2** : évite des tours ou des allers-retours ;
- **P3** : confort.
## P1
### R1. Le `Move` du moteur supprime la destination
- **Fait** : `IFileManager::Move(Dest, Src, Replace=true, …)` supprime d'abord la destination (`Engine/Source/Runtime/Core/Private/HAL/FileManagerGeneric.cpp:303`). E5 a donc choisi `Move(…, false, false, false, true)` (`LPCityMapsDownload.cpp:109`) pour ne jamais rien supprimer. Vu en relecture, avant tout dégât.
- **Règle** : dans un code qui « ne supprime jamais », `Replace` est toujours `false`. On écarte d'abord la destination existante par un déplacement horodaté.
- **Où** : `lp-ue-compat`, nouvelle section « API de fichiers » ; rappel dans `sk-change-safety`.
- **Texte** : « `IFileManager::Move` avec `bReplace=true` supprime la destination avant de déplacer (`FileManagerGeneric.cpp:303`). Pour une pose sans suppression : `Move(Dest, Src, /*Replace*/false, /*EvenIfReadOnly*/false, /*Attributes*/false, /*bDoNotRetryOrError*/true)`, après avoir déplacé l'ancienne destination dans un dossier horodaté. Source : MAPDL1-E5. »
### R2. OpenSSL dans un module du jeu : conflit de `UI`
- **Fait** : E2 bis a échoué sur une erreur unique, `openssl/ossl_typ.h(144,22): error C2365: 'UI' : redéfinition ; la précédente définition était 'espace de noms'` (`MAPDL1-E2B-BUILD.md`). La correction est la parade du moteur, `#define UI UI_ST` avant les en-têtes et `#undef UI` après (`BuildPatchServices/Private/Common/Crypto.cpp:6-12`). E2 ter a ensuite rendu code 0.
- **Règle** : on n'inclut jamais `openssl/*.h` sans cet encadrement. Avant de lancer la build, on cherche par Grep tous les fichiers qui incluent OpenSSL : une seule build doit suffire.
- **Où** : `lp-ue-compat` (ruptures connues) et `ue-cpp-build` (C2365).
- **Texte** : « OpenSSL : `#define UI UI_ST` / `THIRD_PARTY_INCLUDES_START` / `#include "openssl/evp.h"` / `THIRD_PARTY_INCLUDES_END` / `#undef UI`. Sans cela : C2365 `'UI'` (le typedef d'OpenSSL heurte l'espace de noms `UI` d'UE). Modèle : `Engine/Source/Runtime/Online/BuildPatchServices/Private/Common/Crypto.cpp:6-12`. »
### R3. `Height` d'un UStreetMap est en centimètres
- **Fait** : `StreetMap.h:304` annonce des « meters », mais l'import écrit des centimètres (`StreetMapFactory.cpp:387`). Le GeoJSON de S7 en tient compte : `height_m = Height / 100` (`MAPDL1-S7-IMPL.md` § 2).
- **Règle** : toute lecture de `FStreetMapBuilding::Height` passe en centimètres, quel que soit le commentaire.
- **Où** : `lp-city` § 2, tableau des pièges de géométrie.
- **Texte** : « `FStreetMapBuilding::Height` est en **cm**, malgré le commentaire « meters » (`Public/StreetMap.h:304` ; écrit en cm par `StreetMapImporting/StreetMapFactory.cpp:387`) | établi, MAPDL1-S7 ».
### R4. Une session compile le code posé par une autre
- **Faits** :
- La build de l'éditeur de COM31 a rendu `Target is up to date`, avec 0 unité. La ligne R-1 avait en effet déjà été compilée à 18:23 par la build de la 1.7.4 de Paris : la DLL de l'éditeur était plus récente que la source (`MAPDL1-COM31-BUILD.md`). `lp-builder` y a vu un « échec probable » et s'est arrêté.
- W1 et W2, posés par MAPDL1 sans être compilés, ont été compilés par le build de la 1.7.7 de STM1 (`STM1-155`, code 0).
- MAPDL1 et STM1 se sont échangé leurs listes de fichiers : aucun recouvrement.
- **Règles** :
- (a) Avant d'appeler « échec » un `Target is up to date`, on compare la date de la DLL ou de l'exe à celle de la dernière source modifiée.
- (b) Tout code posé non compilé est annoncé aux autres sessions, avec ses MD5 : leur build le compilera, et une casse doit revenir à son auteur.
- (c) Avant d'écrire dans `Source\`, chaque session publie sa liste de fichiers et attend le signal de Big Boss.
- **Où** : `lp-build` § 8 pour (a) ; `lp-lot/boss.md` pour (b) et (c) ; agent `lp-builder` pour (a).
- **Texte (lp-builder)** : « `Target is up to date` avec 0 unité n'est pas un échec si la DLL ou l'exe est plus récent que la source modifiée : une autre session a déjà compilé. Relève les deux dates (Get-Item), rapporte, et continue la fiche si elle le prévoit. »
- **Texte (boss.md)** : « Code posé non compilé : son auteur l'annonce à Big Boss et aux sessions qui compilent (fichiers et MD5). La première build qui le compile rapporte toute erreur à l'auteur. Écriture dans `Source\` : liste des fichiers échangée entre les sessions, puis signal de Big Boss. »
### R5. Reprise à l'identique d'une ancienne carte UE4
- **Faits** :
- Les cartes « NEW Maps » de Mega sont des UStreetMap UE4 cuits, non versionnés (legacy −7, ue4 = 0, drapeaux 0x80000000), avec des propriétés étiquetées. On les lit hors moteur (`LireCarteUE4.py`) et on les reprend par réflexion (`LPImportLegacyStreetMap`, `MAPDL1-P1-IMPL`).
- Le maillage `LPBuildCityStatic -Flat` passe la comparaison avec Londres (1,59 %). Le réglage par défaut et `-Unlit` échouent (58 %).
- `-Unlit` réduit le nombre de sommets, pas celui des triangles.
- Formule des triangles classiques : `3·Pb − 2·B`, écart observé de 0 à −0,18 %.
- Poids de la 3D classique : environ 20 octets par triangle dans l'archive (`MAPDL1-AUTO-FIN` § 2).
- Un asset pilote laissé sous `Content\` a cassé la cuisson des packs (`DirectoriesToAlwaysCook` couvre `/Game`).
- **Règles** :
- reprise par réflexion, sans conversion ;
- `-Flat` pour le maillage statique repris ;
- aucun asset de travail sous `Content\` entre deux sessions ;
- fourchettes de contrôle tirées de la formule.
- **Où** : `lp-city`, nouvelle section « 9. Reprise d'une ancienne carte LifePlay (Mega) » ; `lp-commandlets`, table des cartes, avec une ligne `LPImportLegacyStreetMap`.
- **Texte (lp-city § 9)** : « Ancienne carte Mega = UStreetMap UE4 cuit, non versionné (legacy −7, ue4 = 0, drapeaux 0x80000000), propriétés étiquetées. Lecture hors moteur : `Tools\LireCarteUE4.py --totaux` ; reprise : `LPImportLegacyStreetMap` (réflexion, `-DryRun`) ; chaîne : `RepriseCarte.ps1` (Lire, Creer, Importer, Classic, ODbL). Maillage statique : `-Flat` (Londres 1,59 % ; défaut et `-Unlit` 58 %). Triangles classiques ≈ `3·Pb − 2·B` (écart de 0 à −0,18 %) ; environ 20 o par triangle dans l'archive. Jamais d'asset de travail sous `Content\` (cuisson cassée, MAPDL1-P1). Les données (sans lat/lon ni identifiants OSM) se livrent en GeoJSON en plan local (SPEC-07). »
## P2
### R6. Exécutants haiku et chemins sous Git Bash
- **Faits** :
- EXEC-1 s'est arrêté sur un code 64 : le chemin du script, qui contient une espace, n'était pas entre guillemets dans Git Bash (`MAPDL1-AUTO-EXEC-1.md`).
- Avec un chemin absolu entre guillemets et des barres obliques, le lot 1 est passé (5 villes sur 5) sans aucune décision à prendre (`AUTO-FIN` § 2).
- `Snapshot.ps1` ne pose pas de code de sortie : il faut `$global:LASTEXITCODE = 0` avant l'appel.
- Un `if` à un seul élément devient un scalaire : il faut `@(if …)` (`JOURNAL-BROUILLON`, 14:15).
- **Règles** :
- une brève pour haiku donne chaque commande **en entier**, avec un chemin absolu entre guillemets et des `/` ;
- elle ne laisse aucun choix ;
- elle donne le code attendu et l'arrêt sur tout écart.
- **Où** : `sk-windows-shell` ; agent `lp-builder` ; `sk-orchestrate` (fiche pour un modèle réduit).
- **Texte (sk-windows-shell)** : « Git Bash et chemin avec espace : `pwsh -NoProfile -File "D:/Game/LifePlay-master 5.8/Tools/X.ps1"` (guillemets, `/`). Sans guillemets : code 64. `Tools\Snapshot.ps1` ne pose pas de code : `$global:LASTEXITCODE = 0` avant l'appel. PowerShell : un `if` qui rend un seul élément est un scalaire ; écrire `@(if …)`. »
### R7. Dépassement du budget de tours (S7 : 62 pour 40)
- **Fait** : `lp-impl` (sonnet) a utilisé 62 tours pour 40 (`MAPDL1-S7-IMPL.md`). Causes déclarées :
- débogage de commandes inline ;
- l'unité de `Height` ;
- des essais ajoutés hors de la fiche (un vrai passage d'ODbL en plus du `-ABlanc` demandé).
La fiche couvrait 3 outils liés physiquement, l'écriture en place et 3 villes d'essai, au niveau N3 avec 40 tours.
- **Règles** :
- une fiche qui touche 3 outils avec des essais mesurés reçoit 60 tours ;
- les commandes répétées passent par un script ponctuel `Saved\<lot>\` dès le 2e essai, pas en inline ;
- aucun essai hors de la fiche sans le noter comme écart **avant** de le lancer.
- **Où** : `sk-orchestrate` (barème du budget) ; agent `lp-impl` ; `sk-protocol` § 8.
- **Texte (lp-impl)** : « Une commande longue ou répétée (pwsh inline, chemins avec `\`) passe par un script ponctuel `Saved\<lot>\` dès le 2e essai. Un essai que la fiche ne demande pas ne se lance pas ; on le propose dans le compte rendu. »
- **Texte (sk-orchestrate)** : « Budget : 3 fichiers ou plus, plus des essais mesurés = 60 tours (N3), pas 40. »
### R8. Mise en page Slate : contrôle obligatoire hors de la zone qui défile ; habillage des chemins
- **Faits** :
- Dans COM31, la case d'accord était dans la même `SScrollBox` (plafonnée par `SBox::MaxDesiredHeight`) que la licence dépliée : elle passait sous le cadre (cas B2, `TESTS-02` § E).
- `AutoWrapText` ne coupe qu'entre deux mots : un chemin sans espace dépasse (B8).
- Correctifs W1 :
- la case et les messages sortent de la zone qui défile ;
- `.WrappingPolicy(ETextWrappingPolicy::AllowPerCharacterWrapping)` (`STextBlock.h:121`, `TextLayout.h:72`) ;
- la hauteur de la zone suit le viewport (`UWidgetLayoutLibrary::GetViewportScale`).
- **Règles** :
- les contrôles obligatoires (case d'accord, boutons) et les messages ne vont jamais dans une zone qui défile ;
- tout texte qui peut contenir un chemin ou une URL reçoit `AllowPerCharacterWrapping` ;
- une fenêtre se vérifie en 1280×720.
- **Où** : agent `lp-impl` (rubrique Slate et UMG) ; `lp-ue-compat` (API Slate 5.8).
- **Texte** : « Slate : les contrôles obligatoires et les messages hors de la `SScrollBox` (plafonnée par `SBox::MaxDesiredHeight`, qui peut suivre le viewport : `MaxDesiredHeight_Lambda` + `UWidgetLayoutLibrary::GetViewportScale`). Chemin ou URL : `.WrappingPolicy(ETextWrappingPolicy::AllowPerCharacterWrapping)`, car `AutoWrapText` ne coupe qu'entre deux mots. Vérifier en 1280×720. Source : MAPDL1, essai COM31 (B2, B8). »
### R9. Fichier partiel vide après un échec réseau
- **Fait** : `CreateFileWriter(…, FILEWRITE_Append)` crée le fichier, et son dossier, avant le premier octet. L'échec voulu de B5 (DNS) a laissé `Saved\Downloads\lpm_city_manila\…zip.part` de 0 octet dans un dossier visible (B9). Correctif W2 :
- le `.part` est écrit à côté du zip ;
- à l'échec et à l'annulation, un `.part` vide est déplacé dans `_rejet\` (`SetAsideEmptyPart`), sans aucun `Delete`.
- **Règle** : un téléchargement qui échoue sans recevoir d'octet ne laisse ni fichier ni dossier visible. On le range (déplacement), on ne le supprime pas.
- **Où** : agent `lp-impl` ; `lp-ue-compat` (HTTP).
- **Texte** : « HTTP en flux (`SetResponseBodyReceiveStream`) : le `.part` existe dès l'ouverture. À l'échec ou à l'annulation, s'il fait 0 octet, le déplacer dans `_rejet\` ; pas de sous-dossier par téléchargement. Source : MAPDL1 W2. »
### R10. Le cahier d'essai se calcule comme le code
- **Fait** : le cahier attendait « Download (21 MB) » pour 22 075 120 octets. Le jeu affiche 22, car il arrondit au-dessus (`LPCityMapsSubsystem.cpp:734`). C'est le cahier qui avait tort (`TESTS-02` A1).
- **Règle** : une valeur attendue se calcule par la formule du code, qu'on cite, et non de tête.
- **Où** : `lp-doc` (gabarit FIT) ; agent `lp-writer`.
- **Texte** : « Valeur attendue d'un affichage : la calculer par la formule du code, avec fichier:ligne (arrondi, unités). Exemple : 22 075 120 o → « 22 MB » (Mo arrondi au-dessus). »
## P3
### R11. Crochet `ecriture-gardien` et agents de lecture
- **Fait observé dans MAPDL1** : une commande Bash de `lp-validator` (E3 bis) a été refusée par le crochet, à cause d'un accent grave dans le texte. C'était un faux positif, sans effet ; l'agent l'a relancée sans ce caractère (`MAPDL1-E3B-VAL.md`, compte rendu).
- **Non observé dans MAPDL1** : le blocage des agents de **collecte** que cite Big Boss. Big Boss le complète avec ses propres preuves.
- **Règle** : pour un agent sans Edit, pas d'accent grave ni de `>` dans les commandes du shell. Les recherches passent par Grep ou Read.
- **Où** : `sk-protocol` § 7 (agents sans Edit) ; `hooks\cas-v2.jsonl` (cas d'essai).
- **Texte** : « Agent sans Edit : aucune commande shell avec accent grave, `>` ni `->`, même dans du texte cité ; le crochet `ecriture-gardien` les refuse (faux positif, MAPDL1-E3B). Reformuler, ou passer par Grep et Read. »
### R12. Outils liés physiquement : écriture en place
- **Fait** : `IndexCartes`, `LireCarteUE4`, `RepriseCarte` et `RecetteCarte` sont des liens physiques entre le jeu et l'addon. Ils ont été écrits par `[IO.File]::WriteAllBytes`, qui tronque le même fichier. Les MD5 sont identiques des deux côtés et `fsutil` montre 2 liens (`S6-1-IMPL`, `S7-IMPL`). La mémoire du projet le disait déjà ; les fiches l'ont rappelé à chaque fois.
- **Règle** : la règle est déjà en mémoire ; il faut l'écrire dans le skill.
- **Où** : `lp-paths` (Outils du jeu) ; agents `lp-impl` et `lp-writer`.
- **Texte** : « Beaucoup de fichiers de `Tools\` sont liés physiquement à l'addon : Edit et Write rompent le lien. Vérifier par `fsutil hardlink list` ; écrire en place par `[IO.File]::WriteAllBytes(<Tools>, [IO.File]::ReadAllBytes(<brouillon>))` ; relire les 2 liens et les 2 MD5. »
### R13. `EtatDir.ps1` ne connaît pas un nouveau lot
- **Fait** : `Build.ps1` a écrit ses résumés dans `etat\MAPDL1-E2B\`, `etat\MAPDL1-E2T\` et `etat\MAPDL1-COM31\`, à la racine d'`etat\`. Ils ont été déplacés à la clôture (`MAPDL1-BILAN` § 6).
- **Règle** : à l'ouverture d'un lot, on ajoute sa règle à `EtatDir.ps1`, ou on passe `-EtatDir` à chaque `Build.ps1` et à chaque `RunCommandlet.ps1`.
- **Où** : `lp-lot` (Avant toute modification) ; agent `lp-builder` (passer `-EtatDir` si la fiche le donne).
- **Texte** : « Nouveau lot : règle dans `Tools\EtatDir.ps1`, ou `-EtatDir <etat>\<projet>\<lot>` sur chaque `Build.ps1` / `RunCommandlet.ps1`. Sinon, les résumés vont à la racine d'`etat\` (MAPDL1). »
### R14. Avertissement préexistant à séparer d'un effet nouveau
- **Fait** : `Accessed None … property GI in /Game/UI/CityMenu.CityMenu_C` apparaît 5 fois dans l'essai COM31. Il figure déjà dans les 6 derniers journaux de COM30, sans E1 (`TESTS-02` § E).
- **Règle** : avant d'attribuer un avertissement à un lot, on le compte dans les journaux de la build précédente.
- **Où** : `lp-validate` § 3 ; agent `lp-validator`.
- **Texte** : « Avertissement en jeu : le compter dans les journaux de la build d'avant (`Saved\Logs\*.log`) avant de l'attribuer au lot. Exemple : CityMenu `GI` (Accessed None), préexistant à MAPDL1. »
## Fichiers
- A `Docs/Migration/etat/REL/MAPDL1/MAPDL1-REX-2026-10-22.md`
- Aucun skill ni agent n'est modifié.