LifePlay-Revival/Docs/Migration/etat/AG/P7/P7-T7-VAL.md

125 lines
19 KiB
Markdown
Raw Normal View History

Fiche P7-T7 — Niveau N1 — Modèle prévu sonnet | utilisé Sonnet 5.5 (claude-sonnet-5-5) — Agent lp-validator — Skills sk-protocol, sk-recount, lp-validate (+ debug-verification)
# P7-T7-VAL : lecture statique de codeturion/unreal-api-mcp 0.3.1 comme source de signatures UE 5.8 (2026-10-12)
Hors gabarit : aucun. Verdict de la fiche : **NON ADOPTABLE**. Verdict de validation : **PASS-STATIC** (tout est établi par lecture du code et des en-têtes ; rien n'a été exécuté, la part « estimé » est écrite en § 3.6).
Règle absolue tenue : la roue n'a pas été installée ni exécutée (aucun pip, uvx, venv ; aucun module du paquet importé ou lancé ; `essais\P7-T7-codeturion.ps1` non lancé). Le seul interpréteur lancé est `python -I` sur un script d'extraction `zipfile` écrit dans le scratchpad (sous `%TEMP%`), lancé depuis le scratchpad, jamais depuis le dossier extrait.
## 1. Fiches amont relues
- `D:\Game\LifePlay-master 5.8\Docs\Migration\etat\AG\P7\fiches\P7-T7.md` (fiche exécutée, relue en entier ; elle fait foi).
- `D:\Game\LifePlay-master 5.8\Docs\Migration\etat\AG\P7\P7-TACHES.md` ligne 26 (registre : T7 « lancée », 10 signatures annoncées ; la fiche en demande 5, voir Écarts E2).
- Gabarit : `D:\Game\LifePlay-Sandbox\Tools\claude\skills\lp-doc\gabarits.md` (section VAL).
- Aucune IMPL ni BUILD amont : la fiche est un essai de lecture. Les autres livrables P7 (T1, T2, T3a, T4a, T9) ne sont pas des entrées de T7 et n'ont pas été relus.
## 2. Points à confirmer des IMPL
sans objet : aucune IMPL dans la chaîne de T7 (fiche sans « Dépend de », `P7-TACHES.md:26`). Les deux « points amont » de la rubrique 3 de la fiche, repris ici :
| Point amont (fiche § 3) | Verdict | Preuve |
|---|---|---|
| D1 : les surcharges s'écrasent (clé `fqn` + `INSERT OR REPLACE`) | PASS-STATIC : confirmé | § 3.3 D1 |
| D2 : une `UE_DEPRECATED` peut marquer la déclaration voisine | PASS-STATIC : confirmé (et aggravé : fenêtre en lignes N-2..N pour les méthodes simples) | § 3.3 D2 |
## 3. Critères rejoués
### 3.1 Extraction (fiche § 4a)
Trace : `D:\Game\LifePlay_Backup_5.5\P7\whl-lu\` — Fiche P7-T7 — 2026-10-12, soirée
- Commande 1 : `mkdir whl-lu && tar -xf <roue> -C whl-lu` (Git Bash). Obtenu, brut : `tar: This does not look like a tar archive` / `tar: Skipping to next header` / `tar: Exiting with failure status due to previous errors` (exit 2). Le `tar` de Git Bash (GNU) ne lit pas un zip ; aucun fichier créé (dossier resté vide).
- Commande 2 (seconde méthode prescrite par la fiche § 4a, appliquée au lieu de l'arrêt, voir E1) : `python -I p7t7_extract.py <roue> D:\Game\LifePlay_Backup_5.5\P7\whl-lu` lancé depuis le scratchpad ; le script refuse tout chemin hors destination (contrôle de traversée), puis `zipfile.extractall`. Code 0. Sortie : 12 entrées (`__init__.py` 96 o, `db.py` 9790, `header_parser.py` 35560, `ingest.py` 5047, `server.py` 8860, `unreal_paths.py` 7429, `version.py` 7343, `METADATA` 16127, `WHEEL` 87, `entry_points.txt` 109, `LICENSE` 1067, `RECORD` 1013).
- Recompte par une autre méthode : `find whl-lu -type f` = 12 fichiers ; `find whl-lu -name '*.py' | xargs wc -l` = 288 (db) + 1008 (header_parser) + 150 (ingest) + 290 (server) + 205 (unreal_paths) + 213 (version) + 3 (`__init__`) = **2157 lignes**, égal au chiffre de la fiche (rubrique 3). Aucun `__pycache__` (0), preuve qu'aucun module n'a été importé.
- Intégrité : les 11 hachages sha256 de `RECORD` correspondent aux fichiers extraits (boucle `openssl dgst -sha256 -binary | base64`, 11 « OK », 0 « DIFF »). Roue : 29 879 octets, MD5 `24bd778673216792989cb0a6bf6bce94` (relevé après extraction, roue inchangée).
- Métadonnées : `METADATA:1-3` Name unreal-api-mcp, Version 0.3.1 ; `WHEEL` généré par hatchling 1.32.4 ; `entry_points.txt:2-3` `unreal-api-ingest = unreal_api_mcp.ingest:main`, `unreal-api-mcp = unreal_api_mcp.server:main` ; dépendances `METADATA:43-44` `mcp[cli]<2,>=1.8.0`, `python-dotenv>=1.0` (non utilisé : `grep dotenv` sur les .py = 0 résultat) ; aucune occurrence de `subprocess`, `eval(`, `exec(`, `pickle`, `os.system`, `__import__`, `ctypes`, `base64` dans les 7 .py (grep, exit 1).
### 3.2 Relevé des accès réseau (fiche § 4b, critère § 8)
Commande : Grep `http|urllib|requests|socket` sur `whl-lu\unreal_api_mcp`. Sortie brute : 9 lignes, **toutes dans `version.py`** (8, 9, 15-16, 20, 87, 88, 122, 123, 184) ; aucune dans `server.py`, `ingest.py`, `db.py`, `header_parser.py`, `unreal_paths.py`.
| Accès | Source | Quand | Remarque |
|---|---|---|---|
| Téléchargement de `unreal_docs_<version>.db` depuis `https://github.com/Codeturion/unreal-api-mcp/releases/download/db-v1` | `version.py:15-16, 85-94, 169-204` | première ouverture de la base (premier appel d'outil, `server.py:34`) si aucun fichier local | délai 60 s par opération ; écrit dans `~/.unreal-api-mcp/` (`version.py:14, 170`) |
| Requête HEAD de mise à jour, puis téléchargement et remplacement silencieux de la base locale si la taille distante diffère | `version.py:113-147` (appelée `version.py:166`) | à chaque démarrage avec une base locale | délai 3 s ; aucune somme de contrôle ni signature : le contenu de la base (résumés, indices de dépréciation) est servi à l'agent comme « vérité terrain » (`server.py:21-24`) |
| Aucun autre accès | `server.py` (transport `mcp.run()` `server.py:286`, stdio par défaut de la bibliothèque `mcp` : estimé) ; `ingest.py` (hors réseau, lit le disque) | | |
Variables lues : `UNREAL_VERSION`, `UNREAL_PROJECT_PATH` (`version.py:32, 45`), `UNREAL_INSTALL_PATH`, `PROGRAMDATA` (`unreal_paths.py:16-18, 33`). Version par défaut du code : `5.8` (`version.py:18`) ; le README annonce 5.7 (`METADATA:96`) : incohérence documentaire.
### 3.3 Les deux défauts (fiche § 3 et § 8)
**D1, surcharges écrasées : ÉTABLI.**
- `db.py:16-17` : `fqn TEXT PRIMARY KEY` ; `db.py:94` : `INSERT OR REPLACE INTO api_records` ; `db.py:124` : `executemany` dans l'ordre de la liste (le dernier gagne) ; `ingest.py:123` : une seule insertion de tous les enregistrements.
- La clé ne contient pas la signature : `header_parser.py:559` (UFUNCTION : `fqn = f"{owner}::{func_name}"`), `:599` (UPROPERTY), `:705` (méthode simple : `fqn = f"{name}::{func_name}"`), `:868` (Slate). Deux surcharges d'une même classe ont donc le même `fqn`.
- Aucune table des surcharges ; `server.py:164-166` renvoie l'unique ligne sans le dire. Exemple moteur réel : `MeshComponent.h:128-129` (cas 1 ci-dessous).
**D2, dépréciation sur la voisine : ÉTABLI, pour tous les types d'enregistrement.**
- Méthodes simples : `_check_plain_deprecation` (`header_parser.py:409-426`). Calcul tracé à la main sur `MeshComponent.h:127-129` (e_k = fin de la ligne k) : `before_start = source[:pos].rfind("\n", 0, pos-1)` (l. 417) trouve e_(N-2) (le `\n` juste avant la ligne N est exclu par la borne `pos-1`), puis `rfind("\n", 0, before_start) + 1` (l. 419) donne le début de la ligne N-2. La région (l. 422) couvre donc les lignes N-2 à N, soit **la ligne située deux lignes au-dessus** (le docstring l. 412-414 annonce « the two lines immediately above » sans en voir le piège). Une `UE_DEPRECATED` sur une déclaration d'une ligne marque aussi la déclaration d'après : cas typique « surcharge dépréciée suivie de son remplaçant ».
- UFUNCTION / UPROPERTY / UCLASS / USTRUCT / UENUM : `_check_deprecation` (`header_parser.py:392-406`) regarde 300 caractères avant la macro (l. 394) et, selon l'appelant, 200 caractères après la déclaration pour UFUNCTION (l. 569) et 100 pour UPROPERTY (l. 603) : la déclaration précédente comme la suivante peuvent la marquer.
- Les délégués sont toujours `deprecated=0` (`header_parser.py:763`) ; les classes Slate aussi (`:849`) : une dépréciation de ces types est invisible.
- Le motif `_UE_DEPRECATED` (`header_parser.py:146-148`) exige `UE_DEPRECATED(<version>, "<message sans guillemet>")` : tout autre formulaire n'est pas reconnu (estimé, non rencontré dans les 5 cas).
**D3 (nouveau), trous de couverture : ÉTABLI.**
- `header_parser.py:1002-1006` : un fichier sans `UCLASS|USTRUCT|UENUM|UFUNCTION|UPROPERTY|SLATE_BEGIN_ARGS` renvoie `[]`.
- `header_parser.py:900-918, 922-925` : seules les classes `UCLASS`, `USTRUCT` et Slate sont des « régions » ; une classe C++ simple (`IDetailLayoutBuilder`, `FString`) n'est jamais propriétaire, et, dans un fichier qui a une macro, ses méthodes seraient attribuées à la dernière région précédente (estimé).
- `unreal_paths.py:174` : `rglob("*.h")` ne prend pas `*.h.inl` (`FString` est dans `UnrealString.h.inl`).
- `header_parser.py:651` : les constructeurs (`func_name == name`) sont sautés.
- `unreal_paths.py:121-132` : seuls les dossiers `Classes/` et `Public/` sont lus (pas `Private/`, `Internal/`).
**D4 (nouveau), paramètres tronqués : ÉTABLI (mécanisme), signature obtenue estimée.**
- `header_parser.py:91` : `_FUNC_DECL` se termine par `\(([^)]*)\)`, non équilibré, pour les UFUNCTION. Tout paramètre par défaut contenant une parenthèse (`FString("Hello")`, `FLinearColor(...)`, `FVector(...)`) coupe la liste au premier `)`. Les méthodes simples, elles, utilisent l'extraction équilibrée (`header_parser.py:295-321, 689`) : traitement inégal.
### 3.4 Tableau des 5 cas (fiche § 4c, critère § 8)
Déclarations réelles relues dans `D:\Game\Launcher\Engine\UE_5.8\Engine\Source\...` (lecture seule ; chemins relatifs à cette racine).
| fqn | Réel (fichier:ligne moteur) | Base prédite (ligne de code qui fonde) | Écart |
|---|---|---|---|
| `UMeshComponent::GetMaterialRelevance` | `Runtime\Engine\Classes\Components\MeshComponent.h:127` `UE_DEPRECATED(5.7, ...)`, `:128` surcharge `ERHIFeatureLevel::Type` (dépréciée), `:129` surcharge `EShaderPlatform` (valide). Classe : `UCLASS` `:23-24`, seule du fichier (Grep) | **Une seule ligne**, celle de `:129` (même `fqn` `header_parser.py:705`, clé `db.py:17`, le dernier gagne `db.py:94, 124`) : params `InShaderPlatform:EShaderPlatform`, retour `FMaterialRelevance`, spécificateur `virtual`. Drapeau : `deprecated=1` avec le message de `:127`, car la région de `:129` va de `:127` à `:129` (`header_parser.py:417-422`, calcul § 3.3 D2). Résumé vide (`header_parser.py:226-229, 233-239` : la ligne `:127` s'intercale entre le commentaire et la déclaration) | **D1 + D2** : la surcharge dépréciée (`ERHIFeatureLevel`) disparaît ; la surcharge valide, celle qu'il faut employer, est affichée « DEPRECATED : Please use GetMaterialRelevance with EShaderPlatform argument... » (inversion, `server.py:111-116`) |
| `IDetailLayoutBuilder::GetDetailsView` | `Editor\PropertyEditor\Public\DetailLayoutBuilder.h:63-67` (const, `UE_DEPRECATED(5.5)`) et `:74-78` (non const, `UE_DEPRECATED(5.5)`) ; remplaçantes `:61` et `:72` (`GetDetailsViewSharedPtr`). Classe C++ simple `:35` | **Aucune ligne** : le fichier ne contient aucune macro de réflexion (Grep, 0 occurrence) donc `parse_header_file` renvoie `[]` (`header_parser.py:1002-1006`) ; la classe n'est de toute façon pas une région (`:900-918`). `get_deprecation_warnings("GetDetailsView")` répond « n'est pas déprécié (ou introuvable) » (`server.py:275`), ou renvoie d'autres lignes via le repli FTS (`db.py:281-288`) | **D3** : dépréciation 5.5 invisible (faux négatif présenté comme « pas déprécié ») |
| `FSlateFontInfo::FSlateFontInfo` | `Runtime\SlateCore\Public\Fonts\SlateFontInfo.h:245-246`, `:255-256`, `:265-266`, `:275-276` : quatre constructeurs `UE_DEPRECATED(5.6, "Use constructor with FCompositeFont instead.")` ; non dépréciés `:227`, `:236` (+ défaut `:215`). `USTRUCT` `:145`, `struct FSlateFontInfo` `:146` | **Aucune ligne de constructeur** : `func_name == name` → `continue` (`header_parser.py:651`). Le type est indexé (`USTRUCT`, `header_parser.py:478-503`) avec `deprecated=0` (les 300 caractères avant `:145`, soit `:129-144`, ne contiennent pas de `UE_DEPRECATED`). Seul `FSlateFontInfo::FontName_DEPRECATED` (`:205`) est marqué (suffixe, `header_parser.py:605`) | **D3** : les quatre constructeurs dépréciés en 5.6 sont invisibles ; aucun constructeur n'est jamais consultable |
| `FString::ParseIntoArray` | `Runtime\Core\Public\Containers\UnrealString.h.inl:1746` (`const ElementType* pchDelim`) et `:1783` (`DelimArray, NumDelims`) ; voisines `ParseIntoArrayWS` `:1759`, `ParseIntoArrayLines` `:1770`. Aucune dépréciation | **Aucune ligne** : `*.h.inl` non collecté (`unreal_paths.py:174`) ; `UnrealString.h` sans macro (Grep, 0) donc `[]` (`header_parser.py:1002-1006`) ; `FString` non réfléchie (`:900-918`). `get_function_signature` : exact vide, préfixe vide, puis repli FTS « Similar functions » (`server.py:164-184`) | **D3** : API de chaînes centrale absente ; D1 aurait écrasé `:1746`/`:1783` s'ils avaient été lus |
| `UKismetSystemLibrary::PrintString` (témoin) | `Runtime\Engine\Classes\Kismet\KismetSystemLibrary.h:565-566` (`UFUNCTION` + `static ENGINE_API void PrintString(...)`, 7 paramètres), classe `UCLASS` `:101-102` ; aucune `UE_DEPRECATED` en `:540-589` | **Une ligne**, `fqn` exact (`header_parser.py:559`), `deprecated=0` (rien dans les 300 car. avant ni les 200 après, `:394, 569`), inclusion `Kismet/KismetSystemLibrary.h`. Mais `\(([^)]*)\)` (`header_parser.py:91`) s'arrête au premier `)` de `FString("Hello")` (`:566`) : **2 paramètres sur 7** (`WorldContextObject`, `InString`) ; `bPrintToScreen`, `bPrintToLog`, `TextColor`, `Duration`, `Key` perdus (`header_parser.py:566-567`) ; retour `void` | **D4** : le témoin « simple » est faux (signature partielle sans avertissement) ; D1/D2 n'y touchent pas |
### 3.5 Verdict selon le critère de la fiche (rubrique 8)
Règle : ADOPTABLE si aucun défaut ne touche les surcharges ni le drapeau déprécié, sinon NON ADOPTABLE avec le compte des cas touchés.
- **NON ADOPTABLE.** Cas touchés : **5 sur 5** (1 par D1+D2, 3 par D3, 1 par D4).
- Cas portant une `UE_DEPRECATED` réelle dans les en-têtes 5.8 (cas 1, 2, 3) : **3 sur 3** faussés (1 inversé : la surcharge valide donnée pour dépréciée ; 2 absents : dépréciation invisible). C'est la promesse centrale du paquet (`METADATA:61, 150` : « dépréciations »).
- Cas intact quant au drapeau : 1 (PrintString), mais signature tronquée.
### 3.6 Établi / estimé / incertain (sk-protocol § 1)
- **Établi** (code et en-têtes lus, citations ci-dessus) : le mécanisme de D1, D2, D3, D4 ; les déclarations réelles des 5 cas ; les accès réseau ; l'intégrité de la roue (RECORD) ; le décompte des lignes.
- **Estimé** (déduit du code, **non exécuté**) : le contenu exact des enregistrements prédits au tableau 3.4 (calcul d'indices tracé à la main : si une borne `rfind` était décalée d'un caractère, D2 sur la ligne `:129` changerait ; la lecture du code `header_parser.py:416-422` ne laisse pas d'autre lecture plausible) ; la signature tronquée de PrintString ; le transport stdio par défaut de la bibliothèque `mcp` ; l'attribution à la mauvaise classe pour les classes non réfléchies.
- **Incertain** : la base prébâtie téléchargée (`db-v1`) est construite par une CI (`METADATA:63, 120`) dont le code n'est pas dans la roue ; rien ne prouve qu'elle utilise ce `header_parser.py`. Ses défauts réels ne sont pas connus. Essai qui trancherait : `ingest` d'un moteur 5.8 puis requêtes `get_function_signature("UMeshComponent::GetMaterialRelevance")` et `get_deprecation_warnings` sur une base jetable, dans une machine ou un venv isolés, **interdit par la fiche** (décision de l'utilisateur, § 6).
## 4. Diff lu en entier
sans objet : aucune modification d'un fichier suivi par git. `git status --short -- Docs/Migration/etat/AG/P7` : `?? Docs/Migration/etat/AG/P7/` (dossier P7 entier non suivi, le livrable s'y ajoute). Inventaire de ce qui a été écrit :
- `D:\Game\LifePlay_Backup_5.5\P7\whl-lu\` (dossier neuf, hors dépôt) : 12 fichiers, 3 dossiers (`unreal_api_mcp`, `unreal_api_mcp-0.3.1.dist-info`, `licenses`).
- `%TEMP%\claude\D--Game-LifePlay-master-5-8\f3df474e-f4bb-46ba-a76c-69f3498593ce\scratchpad\p7t7_extract.py` (script d'extraction, sous `%TEMP%`).
- Ce livrable.
- Moteur `D:\Game\Launcher\Engine\UE_5.8` : lu seulement (Read, Grep). Copies du jeu, `.mcp.json` : non touchés.
## 5. Verdict
- Fiche P7-T7 : **NON ADOPTABLE** (`codeturion/unreal-api-mcp` 0.3.1 ne doit pas servir de source de signatures UE 5.8 à lp-impl ni lp-cart tel quel). Cinq cas sur cinq touchés ; les trois dépréciations réelles sont perdues ou inversées ; les surcharges s'écrasent.
- Validation : **PASS-STATIC** (critères de la fiche remplis par lecture : deux défauts établis par citation, tableau de 5 lignes, relevé réseau, part estimée écrite ; rien d'exécuté).
- La méthode actuelle du projet (Read et Grep de l'en-tête moteur, `fichier:ligne`) reste la référence ; elle est la seule à voir les surcharges, les `.h.inl` et les dépréciations des constructeurs.
## 6. Points à confirmer par l'utilisateur
- [ ] Accepter le verdict NON ADOPTABLE et ne pas brancher `unreal-api-mcp` dans `.mcp.json` ni dans les copies.
- [ ] Licence : `LICENSE` est MIT (`LICENSE:1-21`) mais le README du paquet ajoute « Free to use ... for any personal or non-commercial purpose. Commercial use requires permission. » (`METADATA:321-323`) ; contradiction non tranchée (sans objet si le paquet n'est pas adopté).
- [ ] Faut-il convertir la part « estimé » en « établi » par un essai d'exécution (ingest sur une base jetable) ? Non nécessaire pour le verdict ; décision de l'utilisateur, hors cette fiche.
- [ ] Verser en `lp-validate` le piège : `tar -xf` de Git Bash ne lit pas un zip (utiliser `python -I` + `zipfile` depuis le scratchpad, ou le `tar` de Windows) ; vérifier `RECORD` par sha256 avant lecture.
Tours : 17 / 30
## Constats
- E1 (écart à la fiche, § 4a) : `tar -xf` a échoué (message § 3.1). La fiche prévoit « arrêt si l'extraction échoue » mais propose aussi, dans la même rubrique, la méthode `zipfile` ; j'ai pris la seconde sans autre changement de périmètre (dossier de destination, interdits identiques). Si l'orchestrateur lit la règle d'arrêt strictement, c'est un écart à arbitrer.
- E2 (registre) : `P7-TACHES.md:26` annonce « 10 signatures comparées » ; la fiche demande 5 cas, 5 traités.
- E3 : `__init__.py:3` déclare `__version__ = "0.1.1"` alors que le paquet est en 0.3.1 ; `python-dotenv` est déclaré (`METADATA:44`) mais jamais importé ; `Provides-Extra: ingest` (`METADATA:45`) sans dépendance associée. Signes d'un paquet Alpha (`METADATA:32`).
- Risque de chaîne d'approvisionnement : la base est un fichier SQLite téléchargé sans somme de contrôle (`version.py:113-147`) et ses textes (résumés) sont servis à l'agent comme instructions de confiance (`server.py:18-25`) : vecteur d'injection de contenu, en plus des défauts d'exactitude.
- Fichiers à commiter : `A Docs/Migration/etat/AG/P7/P7-T7-VAL.md` (dépôt jeu 5.8, branche `ue5.8`) ; `whl-lu` (hors dépôt) et le script du scratchpad ne se commitent pas.