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

26 KiB
Raw Blame History

Fiche MAPDL1-E3B-VAL — Niveau N1 — Modèle prévu haiku | utilisé haiku (claude-haiku-5-5, déclaré par l'environnement ; non vérifié par claude -p) — Agent lp-validator — Skills lp-validate, sk-recount, sk-protocol, debug-verification

MAPDL1-E3B-VAL : validation statique de l'étape E5, téléchargement et pose des packs (2026-10-22)

Hors gabarit : aucun. Gabarit VAL (D:\Game\LifePlay-Sandbox\Tools\claude\skills\lp-doc\gabarits.md) suivi, sections 1 à 6 dans l'ordre. La fiche demande en plus le tableau des critères (§ 5), les MD5 (§ 3), les sorties brutes (§ 3) et les fichiers à commiter (fin de document).

Lecture seule. Aucune compilation, aucun commandlet, aucun éditeur, aucun lancement du jeu, aucun appel réseau. Tools\Build.ps1 -FromLog non lancé : il écrit un résumé dans etat\ (lot MAPDL1-E2T hors table de Tools\EtatDir.ps1, avertissement déjà noté dans MAPDL1-E2T-BUILD.md § 2) ; le recomptage des journaux est fait par Grep (§ 3.10). LPTestStringCompat non lancé (commandlet interdit par la fiche).

Verdict global : PASS-STATIC. Aucun critère de la fiche § 3 n'est contredit. Une réserve de code reste ouverte (R-1, § 5). Les points de jeu sont reportés (§ 2 et § 6).

Relevés : 2026-10-22, mtimes des fichiers E5 entre 16:49:22 et 17:27:49 ; journaux E2T écrits à 17:29:09 et 17:31:09.

1. Fiches amont relues

  • MAPDL1-E5-FICHE.md : lue en entier (127 l.).
  • MAPDL1-E5-IMPL.md : lue en entier (§ 1 à 6, Constats 1 à 3).
  • MAPDL1-E2T-BUILD.md : lue en entier (compilation éditeur et jeu, correctif C2365).
  • MAPDL1-E3-VAL.md : lue en entier (forme du livrable, sections 1 à 7).
  • MAPDL1-JOURNAL-BROUILLON.md : lue en entier (ligne ERR du correctif UI).
  • Instantanés lus : D:\Game\LifePlay_Backup_5.5\MAPDL1-E5\manifest.csv, D:\Game\LifePlay_Backup_5.5\MAPDL1-E2T\manifest.csv ; fichiers …\MAPDL1-E5\Source\LifePlay\LPCityMapsSubsystem.h, LPCityMapsSubsystem.cpp, LifePlay.Build.cs, …\MAPDL1-E5\Content\Data\LPCityMaps.json (diffs § 4) ; …\MAPDL1-E2T\Source\LifePlay\LPCityMapsDownload.cpp (diff § 3.11).
  • Code lu en entier : LPCityMapsDownload.h (180 l.), LPCityMapsDownload.cpp (1406 l.), LPCityMapsWindow.h (67 l.), LPCityMapsWindow.cpp (585 l.), LPCityMapsSubsystem.h (178 l.), LPCityMapsSubsystem.cpp (946 l.), LifePlay.Build.cs (63 l.), LPCityMaps.json (15 l.).
  • Non relus en entier : les deux licences (copies à l'octet, MD5 seulement) ; MAPDL1-E1-IMPL.md et MAPDL1-E4-TESTS.md (Grep seulement).
  • Moteur 5.8 lu (fichier:ligne) : D:\Game\Launcher\Engine\UE_5.8\Engine\Source\Runtime\Core\Public\HAL\FileManager.h:110, 113 (signatures de Copy et Move) ; …\Core\Private\HAL\FileManagerGeneric.cpp:136-147 (Copy), :175-234 (CopyWithProgress, suppression l. 233), :299-325 (Move, suppression de la destination l. 303 si Replace).

2. Points à confirmer des IMPL : un par ligne

Points de MAPDL1-E5-IMPL.md § 5 (V-1 à V-15) et écarts E-1 à E-6 (voir Constats).

Point (E5-IMPL § 5) Verdict Preuve
V-1 includes THIRD_PARTY autour d'OpenSSL et libzip PASS (compilation) LPCityMapsDownload.cpp:16-23 ; journaux E2T sans error C ni error LNK (§ 3.10) ; jeu lié Result: Succeeded (E2T-BUILD § 5).
V-2 push/pop macro de commdlg.h PASS (compilation) LPCityMapsDownload.cpp:29-49 ; 0 warning C dans les deux journaux (§ 3.10).
V-3 BindSP, FTickerDelegate::CreateSP, Async, lambdas PASS (compilation) Journal jeu : Result: Succeeded (ligne 38), unités 1 à 9 compilées (E2T-BUILD § 2).
V-4 attributs *_Lambda Slate PASS (compilation) LPCityMapsWindow.cpp:177-236, :300-304, :334-345, :396-409 ; journaux sans erreur.
V-5 0 avertissement nouveau PASS Grep warning C : 0 dans les deux journaux (§ 3.10). Avertissements UBT Plugin 'HyperAIStudio' connus (E2T-BUILD § 4).
V-6 liaison HTTP, OpenSSL, libzip, Development PASS (liaison Development) LifePlay.exe lié, Result: Succeeded (journal jeu, E2T-BUILD § 5). Liaison Shipping (comdlg32) : reporté (jeu, non compilé).
V-7 Content/Data/Licenses/*.txt dans le pak reporté (cuisson) Config\DefaultGame.ini:75 présent (§ 3.6). Stage UFS récursif non lu ici.
V-8 rendu de la fenêtre reporté (jeu) LPCityMapsWindow.cpp:245-258 (fond, fenêtre, boutons).
V-9 redirections, Range, Content-Range reporté (jeu, réseau) LPCityMapsDownload.cpp:852-859 (Range), :921-926 (URL finale), :962 (Content-Range). Aucun réseau en VAL.
V-10 coupure, reprise, 200 sur reprise, SHA faux, miroir, recours reporté (jeu) :942-952 (200 sur reprise), :1158-1163 (SHA faux), :1009-1031 (URL suivante puis recours).
V-11 pose transactionnelle, retour arrière, _ancien, « Restart required » reporté (jeu empaqueté) ; lecture statique faite :523-597 (pose), :313-325 (retour arrière), LPCityMapsSubsystem.cpp:846-853 (RestartRequired).
V-12 éditeur : « Download only » possible, « Full install » refusé reporté (jeu éditeur) ; statique établi LPCityMapsDownload.cpp:773-778, :1268-1273 ; LPCityMapsWindow.cpp:457-465.
V-13 Deinitialize pendant un téléchargement reporté (jeu) ; statique établi LPCityMapsSubsystem.cpp:299-304 (fermeture) ; LPCityMapsDownload.cpp:609-622 (annulation, .part gardé).
V-14 dépôt : ordre E1, E5, STM1 PASS (statique) ; ordre de commit reporté au gardien git status (§ 3.8) : E5 en ??, LifePlay.Build.cs et DefaultGame.ini en M.
V-15 reste d'E1 (E-1 ticker, V-5 à V-12 d'E1) reporté (E1, jeu) ; diff de DecorateRows lu Le diff E5 n'ajoute à DecorateRows que la branche RestartRequired (LPCityMapsSubsystem.cpp:755-773, § 4).

3. Critères rejoués (commande → résultat)

3.1 MD5 (Get-FileHash -Algorithm MD5), comparés à MAPDL1-E5-IMPL.md § 1

C1070FF639EF5710578BA33FB32E51D4  …\Source\LifePlay\LPCityMapsDownload.h
8319FC65523AF62DEE70A41ED98217D0  …\Source\LifePlay\LPCityMapsDownload.cpp
E2F30E7FE5814C99E7816D42DF44FBDD  …\Source\LifePlay\LPCityMapsWindow.h
094FF220C4827C11594338163085408E  …\Source\LifePlay\LPCityMapsWindow.cpp
7B7222797ED66A664F157058D3268228  …\Source\LifePlay\LPCityMapsSubsystem.h
568B7217AC1B456096E6FE47A74839CB  …\Source\LifePlay\LPCityMapsSubsystem.cpp
38C28745AC538EE3A643A016A125EA16  …\Source\LifePlay\LifePlay.Build.cs
00298B7B8846244BD4992D61A8C306BD  …\Content\Data\LPCityMaps.json
B1DAB51315B0BA903B527ECD5A988666  …\Content\Data\Licenses\OpenStreetMap_Geodata_License.txt
FB4E3868914D1822BA2974106C0FBDFE  …\Content\Data\Licenses\Vinfamy_License.txt
B1DAB51315B0BA903B527ECD5A988666  …\Licenses\OpenStreetMap_Geodata_License.txt
FB4E3868914D1822BA2974106C0FBDFE  …\Licenses\Vinfamy_License.txt

Résultat : 11 fichiers identiques à IMPL § 1 (« après »), à la lettre. Seul écart : LPCityMapsDownload.cpp, MD5 8319FC65… au lieu de 0F5D9DDD… (IMPL), écart attendu et permis par la fiche (correctif UI). Preuve que c'est le seul changement : § 3.11.

Fichiers STM1 (Get-FileHash, 9 fichiers) : CC146CC6… LPCityWaterSubsystem.cpp, 4A6C6711… .h, E5027FD7… LPCityWaterMask.h, F3783338… LPCityNamesSubsystem.cpp, 10639483… .h, 01CC49D8… LPCityHouses.h, B4FF95AE… LPCityLandmarks.h, 7B44F5FA… LPAssetRegistryHelpers.cpp, 8E3B725A… .h. Résultat : 9/9 identiques à MAPDL1-E5-IMPL.md Constats 2 (valeurs complètes relues). Référence « avant » indépendante : absente (IMPL § 1 ne couvre pas STM1) ; dates d'écriture STM1 antérieures à l'instantané E5 (MAPDL1-E3-VAL.md § 3.3).

3.2 Fins de ligne : Tools\FileCheck.ps1

Commande : pwsh -NoProfile -Command "& 'D:\Game\LifePlay-master 5.8\Tools\FileCheck.ps1' -Path @(…10 chemins…)"

LF      0  180  0 -   C1070FF639EF5710578BA33FB32E51D4        Source\LifePlay\LPCityMapsDownload.h
LF      0 1406  0 -   8319FC65523AF62DEE70A41ED98217D0        Source\LifePlay\LPCityMapsDownload.cpp
LF      0   67  0 -   E2F30E7FE5814C99E7816D42DF44FBDD        Source\LifePlay\LPCityMapsWindow.h
LF      0  585  0 -   094FF220C4827C11594338163085408E        Source\LifePlay\LPCityMapsWindow.cpp
LF      0  178  0 -   7B7222797ED66A664F157058D3268228        Source\LifePlay\LPCityMapsSubsystem.h
LF      0  946  0 -   568B7217AC1B456096E6FE47A74839CB        Source\LifePlay\LPCityMapsSubsystem.cpp
LF      0   63  0 -   38C28745AC538EE3A643A016A125EA16        Source\LifePlay\LifePlay.Build.cs
LF      0   15  0 -   00298B7B8846244BD4992D61A8C306BD        Content\Data\LPCityMaps.json
LF      0  155  0 -   B1DAB51315B0BA903B527ECD5A988666        Content\Data\Licenses\OpenStreetMap_Geodata_License.txt
LF      0    5  0 -   FB4E3868914D1822BA2974106C0FBDFE        Content\Data\Licenses\Vinfamy_License.txt
fichiers=10 LF=10 alertes=0 introuvables=0

Résultat : 0 alerte (critère de la fiche § 1 : 0 alerte attendue). Lignes CR = 0 et BOM = 0 sur les 10 fichiers. Code de sortie : l'outil Bash n'a pas rendu de code de sortie pour cette commande ; le résumé « alertes=0 » est la preuve retenue.

Note : LPCityMapsDownload.cpp = 1406 l. (IMPL : 1403) ; les 3 lignes d'écart sont celles du correctif (§ 3.11).

3.3 Test-Json de LPCityMaps.json et champs fichier

Commande : Get-Content -Raw -LiteralPath '…\Content\Data\LPCityMaps.json' | Test-Json, puis lecture des champs.

True
osm -> Data/Licenses/OpenStreetMap_Geodata_License.txt
lifeplay_ancienne -> Data/Licenses/Vinfamy_License.txt

Résultat : JSON valide. Les deux chemins pointent sur Content\Data\Licenses\ (fichiers présents, § 3.1 et § 3.7).

3.4 Critère a : suppressions et Move

  • Grep Delete|DeleteDirectory|RemoveDirectory|DeleteFile|\.Delete\( sur Source\LifePlay\LPCityMaps* : aucun résultat.
  • Grep Remove (même périmètre, LPCityMapsDownload.*) : 4 occurrences, toutes FTSTicker::GetCoreTicker().RemoveTicker(...) (LPCityMapsDownload.cpp:615, 1039, 1094, 1329). Ce ne sont pas des suppressions de fichier.
  • Grep Copy\(|Move\(|Replace : Move une seule fois, LPCityMapsDownload.cpp:112 : IFileManager::Get().Move(*Destination, *Source, false, false, false, true). Tous les appels passent par MoveFileSafe (:110-113), donc Replace = false (3e argument) partout. Copy : LPCityMapsDownload.cpp:1235 : FileManager.Copy(*Final, *SourcePath, false).
  • Moteur : FileManager.h:113 (Move(Dest, Src, Replace=1, EvenIfReadOnly=0, Attributes=0, bDoNotRetryOrError=0)) ; FileManager.h:110 (Copy(…, Replace=1, …)). Le Replace vrai supprime la destination (FileManagerGeneric.cpp:303) : évité ici.

Résultat : établi. Réserve R-4 : la copie du moteur supprime sa propre destination partielle en cas d'échec (FileManagerGeneric.cpp:231-234) ; cette destination est neuve (la version précédente est mise de côté avant, LPCityMapsDownload.cpp:1229-1233), et la source (fichier du joueur) n'est jamais supprimée.

3.5 Critère 4 (compilation sans l'addon)

  • Grep LPSandbox|LPModTools|LifePlaySandbox sur Source\LifePlay\LPCityMaps* : aucun résultat. Grep LPModInfo|#include "LPMod : une occurrence en commentaire (LPCityMapsWindow.h:2) et LPCityMapsSubsystem.cpp:21 (#include "LPModGuard.h").
  • Glob **/LPModGuard.h sous Source : Source\LifePlay\LPModGuard.h, dans le module du jeu (pas l'addon).
  • LifePlay.Build.cs lu en entier (63 l.) : aucune dépendance LPSandbox ni LPModTools ; dépendances E5 "HTTP", "OpenSSL", "libzip" à :49.

Résultat : établi.

3.6 Critère i : DirectoriesToAlwaysStageAsUFS

Grep DirectoriesToAlwaysStageAsUFS|LPModGuard|LPSandbox|LPModTools sur Config\ : Config\DefaultGame.ini:75 = +DirectoriesToAlwaysStageAsUFS=(Path="Data").

Résultat : établi.

3.7 Mods\ et licences

  • Glob * sous Mods\ : No files found (dossier présent, vide).
  • Glob * sous Content\Data\Licenses\ : Content\Data\Licenses\OpenStreetMap_Geodata_License.txt, Content\Data\Licenses\Vinfamy_License.txt (deux fichiers, aucun autre).

3.8 État git (lecture seule : git status --short, git diff --stat)

 M Config/DefaultGame.ini
 M Source/LifePlay/LifePlay.Build.cs
?? Content/Data/
?? Source/LifePlay/LPCityMapsDownload.cpp
?? Source/LifePlay/LPCityMapsDownload.h
?? Source/LifePlay/LPCityMapsSubsystem.cpp
?? Source/LifePlay/LPCityMapsSubsystem.h
?? Source/LifePlay/LPCityMapsWindow.cpp
?? Source/LifePlay/LPCityMapsWindow.h

 Config/DefaultGame.ini            | 1 +
 Source/LifePlay/LifePlay.Build.cs | 8 ++++++++
 2 files changed, 9 insertions(+)

Résultat : aucun fichier hors périmètre parmi les fichiers E5 ; Mods\ absent de git status (vide, § 3.7). Pas de Blueprint, pas de fichier de Tools\.

3.9 Dates d'écriture et ordre compilation / modification

2026-10-22 16:49:22  6836   Source\LifePlay\LPCityMapsDownload.h
2026-10-22 17:27:49  42383  Source\LifePlay\LPCityMapsDownload.cpp
2026-10-22 16:55:24  2530   Source\LifePlay\LPCityMapsWindow.h
2026-10-22 17:10:54  20313  Source\LifePlay\LPCityMapsWindow.cpp
2026-10-22 17:11:35  7214   Source\LifePlay\LPCityMapsSubsystem.h
2026-10-22 17:12:39  30413  Source\LifePlay\LPCityMapsSubsystem.cpp
2026-10-22 17:12:50  3300   Source\LifePlay\LifePlay.Build.cs
2026-10-22 17:12:50  693    Content\Data\LPCityMaps.json
2026-10-22 17:29:09  6346   Saved\Logs\portage\MAPDL1-E2T_Build_20261022-172856.log
2026-10-22 17:31:09  2780   Saved\Logs\portage\MAPDL1-E2T_Build_20261022-172922.log

Résultat : la dernière écriture d'un fichier E5 (17:27:49) précède le début de la compilation éditeur du journal 172856 (17:28:56 d'après son nom). La compilation porte donc sur le contenu actuel. Les fichiers modifiés après la compilation : aucun.

3.10 Recomptage des journaux de compilation (Grep, sans Build.ps1 -FromLog)

Commande : Grep error C|error LNK|warning C|Result:|Total execution time|Using Visual Studio sur les deux journaux.

MAPDL1-E2T_Build_20261022-172856.log  (éditeur)
33:Using Visual Studio 14.50.35739 toolchain (…\MSVC\14.50.35717) and Windows 10.0.22621.0 SDK (…)
52:Result: Succeeded
53:Total execution time: 12.02 seconds

MAPDL1-E2T_Build_20261022-172922.log  (jeu Development)
14:Using Visual Studio 14.50.35739 toolchain (…\MSVC\14.50.35717) and Windows 10.0.22621.0 SDK (…)
38:Result: Succeeded
39:Total execution time: 105.68 seconds

Résultat : 0 error C, 0 error LNK, 0 warning C dans les deux journaux. Concordance avec MAPDL1-E2T-BUILD.md § 4 (lignes 52-53 et 38-39, Result: Succeeded). Les chiffres du résumé E2T (0 erreur, 0 avertissement compilateur, 1 et 9 unités) ne sont pas recomptés ici : ce recomptage porte sur les lignes d'erreur et de résultat.

3.11 Correctif UI : diff contre l'instantané E2T (seule différence)

Commande : git diff --no-index -- "…\MAPDL1-E2T\Source\LifePlay\LPCityMapsDownload.cpp" "…\Source\LifePlay\LPCityMapsDownload.cpp" (MD5 de l'instantané : 0F5D9DDD3614CFE23FCCD0EA55A3B7B1, d'après MAPDL1-E2T\manifest.csv et Get-FileHash).

@@ -14,10 +14,13 @@
 #include "Misc/Paths.h"
 
 // OpenSSL (EVP, SHA-256) et libzip : avant les en-tetes Windows …
+// UI : le typedef d'OpenSSL heurte l'espace de noms UI du moteur (C2365, E2 bis) ; meme parade que BuildPatchServices/Crypto.cpp.
+#define UI UI_ST
 THIRD_PARTY_INCLUDES_START
 #include "openssl/evp.h"
 #include "libzip/zip.h"
 THIRD_PARTY_INCLUDES_END
+#undef UI
 
 #if PLATFORM_WINDOWS

Résultat : établi. Trois lignes ajoutées (commentaire, #define UI UI_ST avant les en-têtes OpenSSL, #undef UI après THIRD_PARTY_INCLUDES_END), aucune ligne retirée, aucune autre modification. Les lignes 16 à 25 du fichier actuel contiennent ce correctif et rien d'autre. Code de sortie git diff --no-index : 1 (différences, pas une erreur).

3.12 Diffs des fichiers suivis contre les instantanés E5 (décompte recalculé)

21  3   …\MAPDL1-E5\Source\LifePlay\LPCityMapsSubsystem.h   => …\Source\LifePlay\LPCityMapsSubsystem.h
109 5   …\MAPDL1-E5\Source\LifePlay\LPCityMapsSubsystem.cpp => …\Source\LifePlay\LPCityMapsSubsystem.cpp
4   0   …\MAPDL1-E5\Source\LifePlay\LifePlay.Build.cs       => …\Source\LifePlay\LifePlay.Build.cs
2   2   …\MAPDL1-E5\Content\Data\LPCityMaps.json            => …\Content\Data\LPCityMaps.json

Concordance avec MAPDL1-E5-IMPL.md § 1 : +21 −3, +109 −5, +4 −0, +2 −2 : identique. Diff du JSON : seules les lignes 7 et 11 ("fichier") changent ; titre inchangé (voir § 4).

4. Diff lu en entier (fichiers, compte)

Fichier État Compte Lu
Source/LifePlay/LPCityMapsDownload.h neuf (??) 180 l. en entier
Source/LifePlay/LPCityMapsDownload.cpp neuf (??) 1406 l. (1403 + 3 du correctif) en entier ; diff E2T lu (§ 3.11)
Source/LifePlay/LPCityMapsWindow.h neuf (??) 67 l. en entier
Source/LifePlay/LPCityMapsWindow.cpp neuf (??) 585 l. en entier
Source/LifePlay/LPCityMapsSubsystem.h neuf (??, E1 + E5) 178 l. ; E5 : +21 −3 en entier ; diff E5 lu (§ 3.12)
Source/LifePlay/LPCityMapsSubsystem.cpp neuf (??, E1 + E5) 946 l. ; E5 : +109 −5 en entier ; diff E5 lu, 5 hunks (imports, RestartRequired, SortGroup, clic, Deinitialize, Tick, DecorateRows, OpenCityWindow, MarkRestartRequired)
Source/LifePlay/LifePlay.Build.cs M 63 l. ; E5 : +4 −0 ; contre HEAD : 8 insertions en entier ; diff E5 lu
Content/Data/LPCityMaps.json neuf (??) 15 l. ; E5 : +2 −2 en entier ; diff E5 lu
Content/Data/Licenses/*.txt (2) neufs (??) 155 l. et 5 l. MD5 seulement (copies à l'octet)
Config/DefaultGame.ini M +1 contre HEAD ligne 75 seule (Grep, § 3.6) ; diff E1 lu dans E3-VAL § 3.4

Ajouts de E5 hors fiche : aucun. Modifications faites par le validateur : aucune (§ Constats).

5. Verdict et contrôles de la fiche (§ 3 de la fiche)

# Critère (fiche E5 § 3 et § 2) Verdict Preuve (fichier:ligne)
a Aucun Delete / DeleteDirectory / Remove (fichier) ; Move avec Replace=false établi § 3.4 : aucun Delete ; Move unique LPCityMapsDownload.cpp:112, Replace=false ; Copy :1235 avec false. Réserve R-4 (moteur, destination partielle).
b Installation complète refusée si GIsEditor : au clic et avant la pose établi LPCityMapsDownload.cpp:773-778 (au clic, avant tout téléchargement) ; :1268-1273 (avant la pose) ; LPCityMapsWindow.cpp:457-465 (appel).
c Membre avec .. ou ., ou chemin absolu : refusé ; pose sous ProjectDir établi, avec réserve R-1 :209-265 : \ et : (:218), chemin absolu (:224), segments .. et . (:236), préfixes autorisés (:246-251). Pose : Root = ProjectDir (:445), destinations Root / Tree / Rel (:572). Racines de l'archive vérifiées avant toute extraction (:389-436).
d SHA-256 contrôlé avant toute pose ; écart : fichier déplacé (pas supprimé), URL suivante établi Pose seulement depuis OnPackageReady (:1248-1263), elle-même après OnVerified conforme (:1138-1201). Écart :1158-1163 : RejectFile (déplacement dans _rejet, :1388-1406) puis NextUrl (:1058-1069). Cache : :1165-1177.
e Seules les URL https acceptées (lecture de l'index) établi LPCityMapsSubsystem.cpp:123-126 (IsHttps), :415-433 (liens et page filtrés à la lecture), LPCityMapsDownload.cpp:808-812 (contrôle repris au départ de l'essai).
f Aucun texte de licence sans son fichier ; accord demandé avant le téléchargement établi LPCityMapsWindow.cpp:71 (non coché à l'ouverture), :199-207 (boutons de téléchargement actifs seulement si bAccepted), :459 (OnStart), :500-539 (texte chargé depuis ProjectContentDir()/<fichier>, :523, sinon Licence text not found, :534).
g Les 2 fichiers de Licenses identiques à l'octet à Licenses\ établi MD5 § 3.1 : B1DAB513… et FB4E3868… des deux côtés.
h LPCityMaps.json valide (Test-Json), champs fichier existants sous Content\ établi § 3.3 (True) ; Content\Data\Licenses\*.txt présents (§ 3.7) ; LPCityMaps.json:7 et :11.
i DefaultGame.ini : +DirectoriesToAlwaysStageAsUFS=(Path="Data") établi Config\DefaultGame.ini:75 (§ 3.6).
4 Compilation sans l'addon : aucun include ni module LPSandbox / LPModTools établi § 3.5 : aucun. LPModGuard.h est dans Source\LifePlay\ (jeu).

Verdict global : PASS-STATIC.

Réserves (pas de contradiction, mais à trancher) :

  • R-1 (ouverte) : LPCityMapsDownload.cpp:236 ne refuse que .. et .. Un segment fait seulement de points, comme ..., passe. Le critère, tel qu'écrit, est satisfait ; le risque est que Windows normalise ... et fasse remonter l'écriture PoseDir / Member.Name (:473) hors de Saved/Downloads/<pack>/_pose. Le comportement de normalisation n'est pas lu dans les sources : incertain. Essai qui tranche : un zip de test à segments ..., posé dans un dossier jetable hors du jeu, ou la lecture de la normalisation Win32 dans les sources du moteur. Recommandation : refuser tout segment fait seulement de points.
  • R-4 (note) : la copie du moteur supprime sa destination partielle en cas d'échec (FileManagerGeneric.cpp:231-234). La destination est neuve ; la source n'est jamais touchée.
  • R-5 (en jeu) : si RejectFile échoue après un SHA faux (:1161), le .part reste et est re-vérifié à l'URL suivante (:814-833, :1113-1136). La pose reste bloquée par le contrôle ; c'est à confirmer en jeu.

Témoin en jeu : à créer. La fiche de tests MAPDL1-E4-TESTS.md ne contient aucun cas E5 (Grep E5|LPCityMapsDownload|téléchargement|Témoin|COMMUN sans résultat). Demande à l'orchestrateur : ouvrir le cas (fiche FIT, gabarit de lp-doc).

6. Points à confirmer par l'utilisateur (cases)

  • R-1 : refuser aussi les segments faits seulement de points (...) ? La correction est à donner à une IMPL ; le validateur ne corrige pas.
  • E-2 : accepter les entrées de dossier parents (LifePlay/, LifePlay/Mods/, LifePlay/Content/, LifePlay/Content/Modules/) ? La fiche § 2.4 disait « chaque nom commence par … ».
  • E-1 : accepter zip_source_win32w_create + zip_open_from_source(ZIP_RDONLY) à la place de zip_open (fiche § 2.4 étape 2) ?
  • Témoin en jeu : cas à créer pour E5 (R8) ; demande à l'orchestrateur.
  • Ordre de commit E1, E5 et STM1 (V-14) : décision du gardien.
  • villes: [] dans LPCityMaps.json : aucune ville téléchargeable tant que l'index n'est pas généré ; l'essai en jeu demande un index d'essai (Saved\LPCityMaps.json).

Points qui ne se vérifient qu'en jeu (non rejoués, non mesurés) : V-6 Shipping (comdlg32), V-7 (licences dans le pak), V-8 (rendu), V-9 (redirections, Range, casse de Content-Range), V-10 (coupure, reprise, 200 sur reprise, SHA faux, miroir, recours), V-11 (pose, retour arrière, _ancien, « Restart required », montage au lancement suivant), V-12 (éditeur), V-13 (Deinitialize), R-1 (test de segments ...), R-5 (.part corrompu), lecture de licence en build empaqueté (FFileHelper sur ProjectContentDir()), sélecteur « Choose file » (GetOpenFileNameW, jamais utilisé dans le moteur, MAPDL1-E5-IMPL.md Constats 1).

Tours : 8 / 40 (décompte de l'agent au moment de l'écriture, ce tour compris ; FicheStats non lancé)

Constats

Écarts à la fiche (déclarés par l'IMPL, vérifiés) :

  • E-1 : zip_source_win32w_create (LPCityMapsDownload.cpp:274) puis zip_open_from_source(…, ZIP_RDONLY, …) (:281), au lieu de zip_open. Non contraire au critère c. Décision § 6.
  • E-2 : dossiers parents admis (:252-262), au lieu du refus littéral de la fiche § 2.4. Non contraire au critère c (aucun .., . ni chemin absolu). Décision § 6.
  • E-3 : pose fichier par fichier (:523-586, IFileManager::Move un fichier à la fois). Établi.
  • E-4 : refus GIsEditor au clic (:773) et avant la pose (:1268), plus tôt que l'étape 1 de la pose. Même résultat. Établi.
  • E-5 : surveillance de Téléchargements démarrée à l'entrée du recours (:1076, LPCityMapsWindow.cpp:553). Établi.
  • E-6 : statut HTTP jugé à la fin (:915-916, :930), sans OnStatusCodeReceived. Établi.

Choix C-1 à C-12 : revus, aucun ne contredit une règle de la fiche. C-12 (fichier du joueur copié, jamais déplacé : :1224-1235) est conforme à la fiche § 2.5.

Modifications faites par le validateur sur la copie : aucune. Commandes de lecture seulement (Get-FileHash, FileCheck.ps1, Test-Json, git status, git diff --stat, git diff --no-index, Grep, Glob, Read). Une commande Bash a été refusée par le crochet ecriture-gardien (accent grave dans la commande, aucun effet sur les fichiers) ; elle a été relancée sans ce caractère. Seul fichier écrit : ce livrable.

Non mesuré : compilation (non relancée, par consigne), Build.ps1 -FromLog (non lancé, § en-tête), LPTestStringCompat (commandlet interdit), LogStats (aucun journal de jeu), réseau, tout comportement en jeu.

Fichiers à commiter : aucun. La VAL ne commite pas ; le livrable MAPDL1-E3B-VAL.md reste hors dépôt jusqu'à décision du gardien.

À verser : aucun nouveau (le piège du recomptage sans Build.ps1 -FromLog est déjà versé par MAPDL1-E3-VAL.md, lp-validate § 2).

Suite : orchestrateur doit trancher R-1, E-1, E-2 (§ 6), ouvrir le cas témoin E5 (R8), et programmer E2 bis (compilation déjà faite par E2T) ou les essais en jeu ; état : code écrit et compilé à code 0 (E2T-BUILD), validation statique PASS-STATIC, jeu non testé.