LifePlay-Revival/Docs/Migration/etat/CG/CG2/CG2-SPEC-01.md
edujiro5972 56dfcfc3d2 LifePlay UE 5.8 : état de développement au 2026-10-20
Tickets : PUB-20261020-T1

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-20 17:35:10 +02:00

20 KiB

Fiche CG2-SPEC-01 — Niveau N3 — Modèle prévu opus xhigh | utilisé claude-opus-5-5 — Session Medium-xHigh · CG2 — Skills lp-mcp (chargé), lp-lot, sk-protocol

CG2-SPEC-01 : corrections Blueprint de la build 1 (cœur et option), forme exécutable lp-mcp (2026-10-19)

Base : CG2-CART-01.md (accepté). Décisions de l'utilisateur transmises par Big Boss (« CG2 - approuvé de Q1 à Q6 ») :

  • Q1 : retrait complet (texte et morph fixe).
  • Q2 : garde seule en B1.
  • Q5 : refuser .. et les séparateurs, en option B1.
  • Q6 : garde unique à l'ouverture de l'écran sans personnage.
  • Q3, Q4 : B2.

Rédigée sans copie UE. Les identifiants de nœuds viennent des lectures MCP du lot CGREF1 (addon, Docs\etat\CG\CGREF1\CGREF1-T3-MCP.md, -T3d-MCP.md, -T3e-MCP.md), à revérifier en tête de session (§ 2, étape 1). Aucun contenu cité (doc 27) : les noms de boutons et de morphs non neutres restent dans ces fiches de l'addon.

1. Objectif mesurable

Sur une build Development de la copie 5.8, avec le scénario S-CG2 du § 7 :

  • 0 Accessed None venant de VisualMorph_C, CharGen_C et CharGenSlider_C (avant : 378 property chargen à l'ouverture du menu de forme du corps sans CharGen, STE-H3i) ;
  • 0 trace de débogage (Hello, test2905) ;
  • le réglage anti-déformation n'applique plus de morph fixe ;
  • la liste des races animales revient au premier élément en fin de liste ;
  • options faites : un nom de sauvegarde contenant .., / ou \ est refusé, et la liste des préréglages n'a plus de doublon.

Abréviations des graphes (EventGraph de chaque Blueprint) :

  • SL = /Game/UI/CharGenSlider.CharGenSlider:EventGraph ;
  • CG = /Game/UI/CharGen.CharGen:EventGraph ;
  • VM = /Game/UI/VisualMorph.VisualMorph:EventGraph ;
  • GI = /Game/BPs/GI.GI:EventGraph ; GISP = /Game/BPs/GI.GI:SavePreset.
  • refPath d'un nœud : <graphe>.K2Node_<Nom>.

2. Fichiers touchés et ordre (agent lp-mcp-bp, éditeur headless, après BUILD-GO)

Fichiers : Content\UI\CharGenSlider.uasset, Content\UI\CharGen.uasset, Content\UI\VisualMorph.uasset ; en option seulement : Content\BPs\GI.uasset.

Étape 0 : préparation

  • Gel annoncé à toutes les sessions (ListAgents).
  • Instantané : .\Tools\Snapshot.ps1 -Lot CG2 -Path Content\UI\CharGenSlider.uasset, Content\UI\CharGen.uasset, Content\UI\VisualMorph.uasset, Content\BPs\GI.uasset.
  • Éditeur headless (lp-mcp § 2), puis test list_toolsets.
  • load_asset des 4 Blueprints.
  • hyper_blueprint_debug_inspect de chacun (référence « avant » des diagnostics).

Étape 1 : lectures de contrôle (aucune écriture). Tout écart arrête la fiche avec un compte rendu.

  • get_node_infos sur chaque nœud cité ci-dessous. Le titre et les liaisons doivent égaler les fiches CGREF1.
  • get_connected_subgraph CG.K2Node_ComponentBoundEvent_85 : 16 nœuds attendus.
  • VM : find_nodes {graph: VM, title: "CharGen"}. On attend les lectures de GI.CharGen (2 références dans le paquet d'après STE-H3i § 3.2). Pour chacune, get_connected_subgraph, puis noter E, le premier nœud à exécution qui consomme la lecture (directement ou par une chaîne pure), et E→, la cible de sa sortie then. Plus de 2 consommateurs, ou un lecteur hors d'EventGraph : arrêt, la SPEC est à compléter.
  • SL : find_nodes {graph: SL, title: "PlayerCharacter"}, puis find_nodes {graph: CG, title: "PlayerCharacter"}. Si chaque CharGenSlider créé reçoit PlayerCharacter à sa création, D23 est sans objet : C2 est alors sauté et ce saut est noté (lp-mcp règle 4 : pas de garde si « vide » n'est pas un état normal).

Étape 2 : CharGenSlider (SL)

Id Défaut Opérations
C1 D19, retrait de la trace get_node_infos SL.K2Node_CallFunction_11 (PrintString, entrée execute ← IfThenElse_5 sortie else) ; noter N = cible de sa sortie then (attendu : le Set de SetCC). break_pins (IfThenElse_5.else → _11.execute) et (_11.then → N.execute) ; connect_pins (IfThenElse_5.else → N.execute) ; delete_node _11, puis ses entrées pures (concaténation, conversion) seulement si aucune autre liaison ne part de leurs sorties
C2 D23, garde (si l'étape 1 ne l'a pas écartée) créer Utilitaires|ContrôleDeFlux|Branche (B23) et Utilitaires|IsValid (entrée ← sortie de SL.K2Node_VariableGet_44, PlayerCharacter) ; break_pins (IfThenElse_5.else → N.execute) ; connect_pins (IfThenElse_5.else → B23.execute), (IsValid → B23.Condition), (B23.then → N.execute). B23.else → nœud suivant les boucles de SetMorphTarget (leur Completed) s'il existe, sinon rien. GetComponentsByClass est pure (§ 6) : la garde se place devant le Set qui l'évalue

Puis compile_blueprint, hyper_blueprint_debug_inspect (aucun diagnostic nouveau) et save_assets ["/Game/UI/CharGenSlider"].

Étape 3 : CharGen (CG)

Id Défaut Opérations
C3 D18 (et D6), Q1 retrait complet break_pins (ComponentBoundEvent_85.then → IfThenElse_37.execute) ; delete_node des 15 autres nœuds du sous-graphe relevé à l'étape 1 (IfThenElse_37, _38, CallFunction_59, _93, _303, _305, _306, _307 et leurs lectures pures). _85 est gardé, sans suite : le réglage ne fait plus rien
C4 D20, échec du cast get_node_infos CG.K2Node_DynamicCast_0 ; créer PrintString (P20 : InString = "CG2 CharGen Construct : cast GI echoue", bPrintToScreen = false, bPrintToLog = true) ; connect_pins (DynamicCast_0.CastFailed → P20.execute). L'exécution s'arrêtait déjà là : le comportement est inchangé, seule une trace est ajoutée
C5 D20 + D25, Q6 garde unique get_node_infos CG.K2Node_IfThenElse_2 ; noter P, la source de son entrée execute (attendu : l'appel PopulateFaceTextures). Créer une Branche (BG) et IsValid (entrée ← sortie GI.CharacterBeingEdited de VariableGet_385 ou _386, lecture pure réutilisable). break_pins (P.then → IfThenElse_2.execute) ; connect_pins (P.then → BG.execute), (BG.then → IfThenElse_2.execute). BG.else → PrintString P25 (log seul, "CG2 CharGen sans personnage edite : boutons d'etat desactives") → chaîne de SetIsEnabled(false) sur les 9 boutons dont les événements sont ComponentBoundEvent_179, _149, _87, _24, _75, _76, _34 (Randomize*), _5 et _10 (ChangeAnimal, ChangeBreed). Le nom de variable de chaque bouton se relève par get_node_infos sur son événement (CGREF1-T3e § 2.1). Avec un personnage valide, rien ne change
C6 D26 get_node_infos CG.K2Node_CallFunction_14 et _15 : l'un est SelectInt. set_pin_value (broche B de ce SelectInt) = 0 ; relecture : la valeur 0 s'affiche
C7 (option) D4, Q5 get_node_infos du sous-graphe de SaveConfirmButton (ComponentBoundEvent_1418 → IfThenElse_366) ; noter S, la chaîne qui alimente Len (texte de FilenameEntry), et F, la cible de IfThenElse_366.else (début du chemin « champ rouge »). Insérer entre IfThenElse_366.then et CallFunction_5375 (GI.SavePreset) une cascade de 3 Branches (pas de OR pur, règle 4), chacune sur Contains(S, x, bUseCase = false) avec x = .., /, \ : vrai → F ; faux → Branche suivante ; la dernière, faux → CallFunction_5375. Le \ saisi par set_pin_value doit se relire comme un seul caractère
C8 (option) D21 a) break_pins (CustomEvent_139.then → CallFunction_3001.execute) ; créer ClearChildren (cible ← LoadScrollBox) ; câbler _139 → ClearChildren → _3001. b) Dans le corps de MacroInstance_64, créer une Branche (BS) dont la condition est la sortie ReturnValue de CallFunction_327 (Split, pure). Câbler corps de boucle → BS.execute ; BS.then → CreateWidget_96 ; BS.else non relié (entrée sautée, la boucle continue)

Puis compiler, inspecter et save_assets ["/Game/UI/CharGen"].

Étape 4 : VisualMorph (VM)

Id Défaut Opérations
C9 STE-H3i, Q2 garde seule Pour chaque E relevé à l'étape 1 : créer une Branche (BV) et IsValid (entrée ← sortie de la lecture GI.CharGen) ; break_pins (source.then → E.execute) ; connect_pins (source.then → BV.execute), (BV.then → E.execute), (BV.else → E→). Avec la référence nulle, l'exécution sautait déjà E (Accessed None) et continuait vers E→ : le résultat est le même, sans l'avertissement (lp-mcp § 4, « cas non valide »). « Vide » est un état normal : CharGen non ouvert dans la session (STE-H3i § 3.1)

Puis compiler, inspecter et save_assets ["/Game/UI/VisualMorph"].

Étape 5 (option) : GI. À faire seulement si aucun autre ticket de la même vague ne touche GI.uasset (§ 4, R2).

Id Défaut Opérations
C10 D3 get_node_infos de GISP : VariableSet_10, CallFunction_81, _88, _89, _91, _0, _86, _1. Supprimer seulement un nœud dont l'entrée execute n'a aucune liaison et dont les sorties ne vont qu'à d'autres nœuds supprimés. Un nœud relié à un chemin atteignable est gardé et noté
C11 D27 GI.K2Node_CallFunction_20748 (toutes broches libres attendues) : delete_node. GI.K2Node_CallFunction_13122 (PrintString, entrée libre, then → _340) : break_pins (_13122.then → _340.execute), puis delete_node _13122 et ses entrées pures exclusives. _340 est gardé

Puis compiler (long), inspecter et save_assets ["/Game/BPs/GI"].

Étape 6 : clôture

  • git status --short -- Content/, puis fermeture de l'éditeur (ou éditeur laissé ouvert si la file MCP continue, lp-mcp § 2).
  • Éditeur fermé : .\Tools\RunCommandlet.ps1 -Lot CG2 -Commandlet CompileAllBlueprints (journal archivé).
  • Livrable CG2-IMPL-01.md : lien relu de chaque opération, MD5 avant / après, diagnostics avant / après.

3. Repli

Toute réponse qui n'est pas un succès explicite arrête la fiche (lp-mcp § 3). Retour à l'octet du Blueprint en cause par Restore.ps1 -Lot CG2, éditeur fermé, ou par le gardien (git) sur ticket. Les Blueprints déjà enregistrés et validés restent ; le compte rendu dit lesquels.

4. Risques et questions

  • R1 : nœuds renumérotés depuis les lectures CGREF1 du 2026-10-16. Parade : contrôle de l'étape 1, arrêt au premier écart.
  • R2 : GI.uasset est partagé par beaucoup de lots ; les options C10 et C11 l'alourdissent (binaire, compilation longue). Parade : les faire seulement si etat\REPO\FILE.md n'a aucun ticket sur GI.uasset dans la vague ; sinon elles passent en B2.
  • R3 (correction de la CART) : PrintString n'imprime rien en Shipping ni en Test (KismetSystemLibrary.cpp:458, § 6). Les traces de D18 et D19 ne se voient donc qu'en Development : la CART les disait « visibles du joueur ». En Shipping, l'effet réel de D18 est le morph fixe appliqué à chaque changement, retiré par C3. Les Accessed None interrompent aussi l'exécution en Shipping : les gardes gardent leur valeur sans journal.
  • R4 : C5 désactive 9 boutons dans un état jamais observé (D20 et D25 : déclenchement estimé). Aujourd'hui, ce même état produit un Accessed None à chaque clic.
  • R5 : la preuve en jeu demande une build Development empaquetée (lp-mcp règle 5 : pas de PIE pour un parcours d'interface).
  • Q7 : après C3, le champ anti-déformation reste affiché sans effet. Faut-il le masquer en B2 ?
  • Q8 : en B2, après lecture de GI.CloseCharGen (non lu), fermer l'écran au lieu de désactiver les boutons ?
  • Q9 : CART et référentiel parlent de 8 boutons ; T3e § 2.1 en liste 9 (RandomizeTattoo compris). C5 les prend tous les 9.

5. Interdits

  • Aucun nœud ni Blueprint hors de cette liste ; aucun C++ ; aucun AND / OR / Select pur pour une garde (cascades de Branches seulement).
  • Aucune avance d'initialisation de widget (ERR-057, lp-mcp § 4).
  • Aucune PIE comme preuve ; aucun commandlet ni build éditeur ouvert ; aucun git qui écrit ; aucun autre asset enregistré.
  • Traces ajoutées en journal seul (bPrintToScreen = false), en ASCII.

6. Valeurs moteur lues (moteur installé 5.8, Engine\Source\Runtime\)

  • UKismetSystemLibrary::PrintString : signature Engine\Classes\Kismet\KismetSystemLibrary.h:566 (bPrintToScreen, bPrintToLog, Duration). Corps vide en Shipping et en Test sauf USE_LOGGING_IN_SHIPPING : Engine\Private\KismetSystemLibrary.cpp:456-458.
  • UKismetStringLibrary::Contains(SearchIn, Substring, bUseCase = false, bSearchFromEnd = false) : KismetStringLibrary.h:353.
  • UKismetStringLibrary::Split(…) rend un bool : KismetStringLibrary.h:524.
  • UKismetSystemLibrary::IsValid : KismetSystemLibrary.h:128.
  • AActor::K2_GetComponentsByClass est BlueprintCallable et const, donc pure en Blueprint : Engine\Classes\GameFramework\Actor.h:3806-3807.
  • UWidget::SetIsEnabled(bool) : UMG\Public\Components\Widget.h:544-545.

7. Critères de VAL (commande → attendu)

# Commande Attendu
V1 git status --short -- Content/ M sur Content/UI/CharGenSlider.uasset, CharGen.uasset, VisualMorph.uasset (+ Content/BPs/GI.uasset si C10 ou C11), rien d'autre
V2 get_node_infos de chaque nœud créé ou relié (IMPL § liens) liaisons conformes au § 2 ; SelectInt.B = 0 ; PrintString ajoutés : écran faux, journal vrai
V3 compile_blueprint + hyper_blueprint_debug_inspect par Blueprint succès ; aucun diagnostic nouveau par rapport à la référence de l'étape 0
V4 RunCommandlet.ps1 -Commandlet CompileAllBlueprints, éditeur fermé 0 erreur ; aucun avertissement nouveau sur les 3 (ou 4) Blueprints
V5 MD5 des .uasset contre le manifeste D:\Game\LifePlay_Backup_5.5\CG2\manifest.csv « avant » = manifeste ; « après » ≠ avant pour les seuls fichiers de V1
V6a jeu Development, menu principal → menu de forme du corps sans ouvrir CharGen ; LogStats.ps1 <journal> -Pattern 'property chargen' 0 (avant : 378)
V6b ouvrir CharGen, bouger 3 curseurs de morph et le réglage anti-déformation ; -Pattern 'Hello|test2905' 0 ; le visage ne change pas par le réglage anti-déformation
V6c cliquer les 9 boutons ; ChangeBreed jusqu'à la fin de la liste retour au premier élément ; -Pattern 'Accessed None.*(CharGen_C|CharGenSlider_C|VisualMorph_C)' → 0
V6d (option) enregistrer sous a/b, ..x, a\b, puis sous un nom normal 3 refus (champ rouge), aucun nouveau fichier sous Content\Modules\ pour eux ; le nom normal est écrit
V6e (option) ouvrir et fermer la liste de chargement deux fois aucune entrée en double
V7 PubCheck et fins de ligne des livrables 0 constat ; LF

V6 est joué par l'utilisateur sur une build Development (Tools\BuildDebug.ps1 -Config Development). Le journal est archivé dans etat\CG\CG2\.

8. Demande de jeton (BUILD-DEMANDE, envoyée à Big Boss)

Une session MCP d'écriture (agent lp-mcp-bp, N3, maxTurns 75) dans la file de la copie UE après STM1 :

  • étapes 0 à 6, environ 5 h pour le cœur et 2 h pour les options ;
  • puis CompileAllBlueprints éditeur fermé ;
  • puis une build Development pour V6, qui peut être mise en commun avec les autres lots du J3.

Fichiers gelés pendant la session : les 4 .uasset du § 2.

9. Révision 2 (2026-10-20), après l'arrêt de CG2-IMPL-01 à l'étape 1

CG2-IMPL-01.md § 0.1 et § 2 (E1 à E8) ont relevé 4 écarts de la SPEC sur la copie inchangée (CharGen.uasset au commit 71511a2 ; MD5 des 4 assets égaux au manifeste CG2). Aucune écriture n'a eu lieu. Les points ci-dessous remplacent le § 2 pour les opérations citées ; le reste du § 2 vaut toujours. Les étapes 0 et 1 sont acquises (IMPL § 0.1) : la reprise commence à l'étape 2.

Op Correction Motif
outils hyper_blueprint_debug_inspect est absent du serveur. Après chaque compile_blueprint {warnings_as_errors: true} (succès = returnValue sans erreur), lire le journal de l'éditeur par EditorToolset.LogsToolset, filtré sur le nom du Blueprint : aucune ligne Error ni Warning nouvelle. type_id en anglais : Utilities|FlowControl|Branch, Utilities|IsValid, Development|PrintString IMPL E4, E7
C1 pas de nœud de concaténation : supprimer _11, puis SL.K2Node_CallFunction_6 (ToString(Float), sortie vers _11 seulement). VariableGet_25 n'est supprimé que si get_node_infos montre qu'il n'a pas d'autre liaison IMPL E6
C2 sauté : les deux créations de CharGenSlider dans CG renseignent PlayerCharacter (VariableSet_10, VariableSet_42). D23 est sans objet (lp-mcp règle 4) IMPL § 0.1 C2
C3 sous-graphe de 14 nœuds : break_pins (ComponentBoundEvent_85#1 → IfThenElse_37#0), puis delete_node des 13 autres : IfThenElse_37, CallFunction_59, VariableGet_280, IfThenElse_38, CallFunction_93, VariableGet_281, VariableGet_282, CallFunction_303, CommutativeAssociativeBinaryOperator_28, CallFunction_305, CallFunction_306, CommutativeAssociativeBinaryOperator_29, CallFunction_307 IMPL E1 : le 16 de T3d est une erreur de compte (estimé). CallFunction_307 n'a pas de liaison d'exécution : la trace de D18 ne s'exécutait pas. L'effet réel de D18 est le seul morph fixe (CallFunction_303), en Development comme en Shipping
C8 a (option) insérer ClearChildren (cible ← LoadScrollBox) entre CG.K2Node_CustomEvent_139#1 (then) et VariableSet_508#0 ; CallFunction_3001 est pure et reste inchangée IMPL E2
C9, lecture 1 VM.VariableGet_16 alimente la condition de la Branche IfThenElse_1. Insérer BV1 entre VariableSet_1#0 (then) et IfThenElse_1#0 : IsValid ← sortie de VariableGet_16 ; BV1.then → IfThenElse_1#0 ; BV1.else → Knot_0#0, la cible de IfThenElse_1.else. Avec la référence nulle, la condition valait faux et l'exécution suivait ce else : le comportement est identique IMPL E3 ; règle ajoutée : une garde devant une Branche relie son else au else de cette Branche
C9, lecture 2 modèle d'origine : BV2 entre Knot_1 (← DynamicCast_1 CastFailed) et CallArrayFunction_0#0 ; IsValid ← sortie de VariableGet_27 ; BV2.then → CallArrayFunction_0#0 ; BV2.else → CallFunction_16#0 IMPL § 0.1 C9
C10, C11 retirées de B1, reportées en B2 avec les autres corrections de GI (D2, D9, D17), pour une seule révision de GI.uasset (52,7 Mo). D3 et D27 sont du code non exécuté, sans effet en jeu. La règle stricte de C10 ne retient d'ailleurs aucun nœud (IMPL E5) ; en B2, seuls GISP.CallFunction_0 et _1 (PrintString, entrée d'exécution libre) seront supprimables. Le gel de GI.uasset peut être rendu coût d'un binaire de 52,7 Mo pour un gain nul en jeu

Hors champ, noté pour B2 : dans VM, la condition de IfThenElse_1 lit aussi VariableGet_15 (IsMale du personnage édité) sans garde, par un AND pur (IMPL E3).

Périmètre de la reprise :

  • cœur : C1, C3, C4, C5, C6 et C9 (2 lectures) ;
  • options : C7 et C8 ;
  • fichiers : CharGenSlider, CharGen et VisualMorph (.uasset) ;
  • V1 attendu : ces 3 fichiers seulement.

10. Révision 3 (2026-10-20), après l'arrêt de la reprise v2

Constats de CG2-IMPL-01 v2 :

  • C1 est faite et enregistrée : CharGenSlider.uasset, MD5 CBE53469FC73E7829A00710126095C7E, compilé, journal propre.
  • C3 a été faite en mémoire seulement. CharGen n'est pas enregistré : l'éditeur a été fermé sans enregistrer, et le MD5 sur disque égale l'instantané.
  • Arrêt : le mode auto de Claude Code a refusé deux fois un create_node dans CharGen. Ce refus attend le geste de l'utilisateur ; il ne se contourne pas.

F2, décision de la SPEC. Le type_id Utilities|IsValid crée la macro IsValid. Cette macro a une entrée d'exécution, une entrée objet et deux sorties d'exécution, Is Valid et Is Not Valid. Elle remplace le couple « Branche + fonction IsValid » partout dans le § 2 et le § 9 (C5, C9 lectures 1 et 2) :

  • l'exécution entrante est câblée sur l'entrée de la macro ;
  • Is Valid remplace la sortie then ;
  • Is Not Valid remplace la sortie else.

Le comportement est le même, avec un seul nœud et sans pure ; la règle 4 de lp-mcp (pas de pure composée) est respectée.

Reprise à faire, sur le fichier de l'instantané : C3, C4, C5, C6, puis les options C7 et C8 dans CharGen ; C9 dans VisualMorph. Elle n'aura lieu qu'après le geste de l'utilisateur sur le refus du mode auto, et avec un nouveau BUILD-GO de Big Boss.