LifePlay-Revival/Docs/Migration/etat/ST/STE/STE-H10-GI2-IMPL.md
edujiro5972 8084622450 LifePlay UE 5.8 : état de développement au 2026-10-19
Tickets : REP1-T4

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-19 22:57:42 +02:00

20 KiB
Raw Blame History

Fiche STE-H10-GI2 — Niveau N3 — Modèle prévu opus (xhigh) | utilisé claude-opus-5-5 — Agent lp-mcp-bp — Skills sk-protocol, sk-change-safety, lp-mcp, lp-lot

STE-H10-GI2-IMPL : B1-a (SavePreset) puis B2-a (GetSMBoolForSMC_Cloth) dans GI, par MCP (2026-10-13)

Résultat : réussi, côté éditeur. Les deux blocs sont écrits, relus conformes, compilés et enregistrés.

Bloc Nœuds Coupures Liens Valeur posée MD5 de GI après save_assets
B1-a, SavePreset 503 → 506 1 4 S3 = true 9a8a61e8…
B2-a, GetSMBoolForSMC_Cloth 11 → 13 2 4 variable locale MeshSuffix 71034a04…

Les trois compilations de GI (avertissements en erreurs) sont sans ligne Error ni Warning dans le journal de l'éditeur. Person et TopRight sont inchangés. La preuve en jeu (build empaqueté + LogStats) n'est pas de mon ressort.

BUILD-GO : « [BOSS→STE] BUILD-GO STE-H10 (a), GI seul » (Big Boss, 2026-10-13), le même que pour STE-H10-GI1. Éditeur headless PID 8296, port 8000, connexion partagée avec la session STE [b84cc5]. Accord de l'utilisateur : « tu peux y aller mcp ouvert ».

Trois passages :

Passage Agent Tours Travail Arrêt
1 a6d948272a33b8bfb 24 écrit le bloc SavePreset et le compile refus du crochet (§ 2)
2 (reprise) ad9203bcfd2e63d2b 15 / 60 recompte, relit, recompile refus du crochet (§ 2)
3 (reprise) ad9203bcfd2e63d2b 35 / 45 enregistre B1-a, écrit et enregistre B2-a —

L'orchestrateur a fait certains contrôles entre les passages 2 et 3. Ils sont cités comme tels.

1. Fichiers modifiés

Fichier modifié : Content\BPs\GI.uasset, deux fois, par save_assets seulement.

Relevés d'empreinte. Commande (Git Bash, copie 5.8) : md5sum Content/BPs/GI.uasset Content/BPs/Person.uasset Content/UI/TopRight.uasset

Moment (+0200) GI Person TopRight
00:44:39, entrée (passage 1) 324a4f271203d4ed8758b6db73ad0a35 d1b4c63d47621b1191ad9352c80c99cd 1fcb94a60061b418e9deeb08bc1a990d
avant le passage 3 (relevé de l'orchestrateur) 324a4f271203d4ed8758b6db73ad0a35 — —
00:51:51, après save_assets n° 1 (B1-a, SavePreset) 9a8a61e8caf7dd09b8f70b1d33847171 d1b4c63d47621b1191ad9352c80c99cd 1fcb94a60061b418e9deeb08bc1a990d
00:54:31, après save_assets n° 2 (B2-a) 71034a04c6d64e1a806ca0dadc934fb9 d1b4c63d47621b1191ad9352c80c99cd 1fcb94a60061b418e9deeb08bc1a990d

save_assets {asset_paths:["/Game/BPs/GI"]} (toolset editor_toolset.toolsets.asset.AssetTools) a rendu {"returnValue":true} les deux fois. Journal de l'éditeur D:\Game\LifePlay-master 5.8\Saved\Logs\LANC1-headless-20261013-001639.log, heures en UTC (locale − 2 h) :

  • n° 1 : ligne 2508, 22.51.43:006, Cmd: OBJ SAVEPACKAGE PACKAGE="/Game/BPs/GI" FILE=".../Content/BPs/GI.uasset" SILENT=true ; ligne 2510, déplacement vers Content/BPs/GI.uasset ;
  • n° 2 : ligne 2591, 22.54.23:201, même commande ; ligne 2593, déplacement vers Content/BPs/GI.uasset.

Seul le paquet /Game/BPs/GI est enregistré dans Content (grep SAVEPACKAGE du journal). Les sauvegardes automatiques Saved\Autosaves\Game\BPs\GI_Auto1.uasset (ligne 2316) et GI_Auto2.uasset (ligne 2498, 22.49.38) sont hors Content ; je ne les ai pas touchées. git status --short -- Content est inchangé : M Content/BPs/GI.uasset, M Content/BPs/Person.uasset, M Content/UI/TopRight.uasset. Person et TopRight étaient déjà modifiés avant cette fiche, et leurs MD5 n'ont pas changé.

1.1 B1-a — /Game/BPs/GI.GI:SavePreset

Comptes : 503 avant, 506 après (établi). find_nodes {graph:{refPath:"/Game/BPs/GI.GI:SavePreset"}, title:""} rend sa liste en ligne. Je l'ai comptée dans la transcription de l'agent qui l'a reçue (dossier %USERPROFILE%\.claude\projects\D--Game-LifePlay-master-5-8\36e41eab-6dbf-4c01-adea-9fe53389b8ce\subagents\), par des tubes, sans écrire de fichier :

pat='GI\.GI:SavePreset\.K2Node_[A-Za-z]*_[0-9]*'
# passage 2, agent-ad9203bcfd2e63d2b.jsonl, ligne du find_nodes :
ligne 56 : total 506, uniques 506
# passage 1, agent-a6d948272a33b8bfb.jsonl :
prec ligne 243 : 503        (avant écriture)
prec ligne 306 : 506        (après compilation, passage 1)
# ( sed -n 243p <prec> | grep -o "$pat" | sort -u ; sed -n 56p <reprise> | grep -o "$pat" | sort -u ) | sort | uniq -u
GI.GI:SavePreset.K2Node_IfThenElse_11
GI.GI:SavePreset.K2Node_VariableGet_2
GI.GI:SavePreset.K2Node_VariableSet_14
# nœuds de la liste avant absents de la liste après :
0

Correspondance SPEC → nom réel → type_id. Préfixe : /Game/BPs/GI.GI:SavePreset.K2Node_. Au passage 1, chaque type_id a été vérifié par find_node_types avant create_node. Au passage 2, type, broches et positions ont été relus par get_node_infos.

SPEC Nœud réel type_id de création Type relu Broches (index) Position
S1 Branche IfThenElse_11 `Utilitaires ContrôleDeFlux Branche` idem
S2 Get SaveBeforeDestroy VariableGet_2 `Variables Pardéfaut ObtenirSaveBeforeDestroy` `
S3 Set RevertSkipped (true) VariableSet_14 `Variables Pardéfaut DéfinirRevertSkipped` `

Nœuds existants, relus avant écriture (passage 1) puis après (passage 2) :

  • IfThenElse_2957 (Branche, 15744, 9248) :
    • then (0) : avant → CallFunction_16039.execute ; après → IfThenElse_11.execute seul ;
    • else (1) non relié ;
    • entrées inchangées : execute ← IfThenElse_8249 (1), CallFunction_0, CallFunction_967 ; Condition ← CallFunction_69701.
  • CallFunction_16039 (|RevertStripAll, 15952, 9232) :
    • execute : avant ← IfThenElse_2957 ; après ← IfThenElse_11.else seul ;
    • self (1) ← VariableGet_9248, inchangé ;
    • then (0) non relié.

Coupure : IfThenElse_2957.then (0) / CallFunction_16039.execute (0), break_pins → null. Relue absente.

Liens (connect_pins → null), relus deux fois par get_node_infos des 5 nœuds :

# Sortie Entrée Relu
1 IfThenElse_2957.then (0) IfThenElse_11.execute (0) oui, seul lien de part et d'autre
2 VariableGet_2.SaveBeforeDestroy (0) IfThenElse_11.Condition (1) oui
3 IfThenElse_11.else (1) CallFunction_16039.execute (0) oui, seule entrée de CallFunction_16039.execute
4 IfThenElse_11.then (0) VariableSet_14.execute (0) oui ; VariableSet_14.then non relié (fin de fonction)

set_pin_value VariableSet_14.RevertSkipped (1) = true → null, relu "value":"true".

1.2 B2-a — /Game/BPs/GI.GI:GetSMBoolForSMC_Cloth

Schéma de add_variable, lu par describe_toolset editor_toolset.toolsets.blueprint.BlueprintTools. La réponse fait 74 787 caractères ; le harnais l'a enregistrée dans ...\tool-results\mcp-unreal-mcp-describe_toolset-1791845542804.txt, et je l'ai lue par grep -o. Extrait : add_variable prend blueprint, name et type_name (requis), plus graph (« If provided, adds a local variable to this function graph instead of a member variable ») et container_type.

Variable locale :

  • avant : list_variables {blueprint:{refPath:"/Game/BPs/GI.GI"}, graph:{refPath:"/Game/BPs/GI.GI:GetSMBoolForSMC_Cloth"}} → [] ;
  • création : add_variable {blueprint:{refPath:"/Game/BPs/GI.GI"}, name:"MeshSuffix", type_name:"string", graph:{refPath:"/Game/BPs/GI.GI:GetSMBoolForSMC_Cloth"}} → null ;
  • après : même list_variables → ["MeshSuffix"] ;
  • find_node_types {graph:<GetSMBoolForSMC_Cloth>, type_id_filter:"MeshSuffix", context_pins:[]} → ["Variables|Pardéfaut|ObtenirMeshSuffix","Variables|Pardéfaut|DéfinirMeshSuffix"].

Comptes : 11 avant, 13 après (établi). Même méthode de comptage, dans agent-ad9203bcfd2e63d2b.jsonl :

pat='GetSMBoolForSMC_Cloth\.K2Node_[A-Za-z]*_[0-9]*'
ligne 102 (avant, find_nodes) : 11 ; ligne 187 (apres, find_nodes) : 13
presents dans une seule liste :
GetSMBoolForSMC_Cloth.K2Node_VariableGet_0
GetSMBoolForSMC_Cloth.K2Node_VariableSet_0
absents de la liste apres :
0

Les 11 nœuds avant : FunctionEntry_2157, FunctionResult_0, VariableGet_4937, MacroInstance_1872, BreakStruct_1008, IfThenElse_2762, CallFunction_0, CommutativeAssociativeBinaryOperator_0, CallFunction_1, CallFunction_2, CallFunction_3.

Correspondance SPEC → nom réel → type_id. Préfixe : /Game/BPs/GI.GI:GetSMBoolForSMC_Cloth.K2Node_.

SPEC Nœud réel type_id de création (vérifié par find_node_types) Type relu Broches (index) Position
C1 Set MeshSuffix VariableSet_0 `Variables Pardéfaut DéfinirMeshSuffix` `
C2 Get MeshSuffix VariableGet_0 `Variables Pardéfaut ObtenirMeshSuffix` `

Nœuds existants relus avant écriture (get_node_infos), conformes à la SPEC § 2.3 :

  • FunctionEntry_2157 (|GetSMBoolForSMC_Cloth, -496, 0) : then (0) → MacroInstance_1872.Exec ; SMC (1) → CallFunction_3.Component.
  • MacroInstance_1872 (Utilities|Array|ForEachLoopwithBreak, -80, 0) : Exec (0) ← FunctionEntry_2157.then ; Array (1) ← VariableGet_4937 ; LoopBody (0) → IfThenElse_2762.
  • CommutativeAssociativeBinaryOperator_0 (Utilitaires|Chaîne|Append, 560, 336) : ReturnValue (0) → CallFunction_2.InSuffix seul ; A (0) et C (2) ← CallFunction_0 ; B (1) = ".".
  • CallFunction_2 (Utilitaires|Chaîne|EndsWith, 496, 496) : SourceString (0) ← CallFunction_1 ; InSuffix (1) ← CommutativeAssociativeBinaryOperator_0 ; SearchCase (2) = IgnoreCase ; ReturnValue → IfThenElse_2762.Condition.
  • Chaîne en amont, lue sans être touchée : CallFunction_3 (GetSkeletalMesh(UE4), Component ← SMC) → CallFunction_0 (GetObjectName) → A et C de l'Append ; CallFunction_1 (ToString(Name), ← BreakStruct_1008 (6)).

Coupures (break_pins → null, un appel chacune), relues absentes :

  1. FunctionEntry_2157.then (0) / MacroInstance_1872.Exec (0) ;
  2. CommutativeAssociativeBinaryOperator_0.ReturnValue (0) / CallFunction_2.InSuffix (1).

Liens (connect_pins → null, un appel chacun), relus par get_node_infos sur les 6 nœuds touchés après le dernier lien :

# Sortie Entrée Relu
1 FunctionEntry_2157.then (0) VariableSet_0.execute (0) oui, seul lien de FunctionEntry_2157.then
2 VariableSet_0.then (0) MacroInstance_1872.Exec (0) oui, seule entrée de MacroInstance_1872.Exec
3 CommutativeAssociativeBinaryOperator_0.ReturnValue (0) VariableSet_0.MeshSuffix (1) oui, seul lien de ReturnValue
4 VariableGet_0.MeshSuffix (0) CallFunction_2.InSuffix (1) oui, seule entrée de InSuffix

Inchangés à la relecture : MacroInstance_1872.Array, LoopBody et Array Element ; CallFunction_2.SourceString, SearchCase (IgnoreCase) et ReturnValue ; FunctionEntry_2157.SMC ; l'Append A, B (".") et C.

1.3 Compilations

Les trois appels : compile_blueprint {blueprint:{refPath:"/Game/BPs/GI.GI"}, warnings_as_errors:true}.

# Passage Réponse Journal (UTC) Error / Warning après
1 1, SavePreset null ligne 2482, 22.46.06 0 (lignes 2483-2489 lues par Read)
2 2, SavePreset null ligne 2492, 22.49.31 0 (lignes 2480-2501, relevé de l'orchestrateur)
3 3, B2-a null ligne 2583, 22.54.08 0 (tail -n +2583 <journal> | grep -i "error|warning" : sortie vide, journal à 2617 lignes) ; tail -n +2536 | grep -i … : vide aussi

Les lignes LogModelContextProtocol: Error: Call to unknown method "server/discover" du journal (2380, 2535) viennent de connexions de clients MCP. Elles ne concernent pas la compilation.

2. Écarts à la SPEC

  • Graphes : aucun écart. Le bloc SavePreset est conforme au § C.5 (second bloc) ; B2-a est conforme au § 2.3 (une variable locale MeshSuffix string, 2 nœuds, 2 coupures, 4 liens, 11 → 13).
  • Procédure, passage 1, écart à la fiche (rubrique 5, « un appel MCP à la fois »).
    • Faits : break_pins (coupure) et le premier connect_pins (IfThenElse_2957.then → IfThenElse_11.execute) sont partis dans le même message. Les deux ont répondu null.
    • Effet : aucun. Dans les deux ordres possibles, l'état final est le même : then est relié à IfThenElse_11 seul, ce que confirment deux relectures. L'ordre d'exécution réel n'a pas été relevé (incertain).
    • Tous les autres appels MCP sont partis un par un. Pour le passage 3, le journal (lignes 2541-2582) montre un seul Dispatching par appel, espacés de 3 à 5 s.
  • Arrêt du passage 1 : refus du crochet ecriture-gardien sur une redirection > $S/sp_apres.txt vers le dossier temporaire. Message exact :
    PreToolUse:Bash hook error: [python -I "$CLAUDE_PROJECT_DIR/Tools/claude/hooks/ecriture-gardien.py"]: Refus du crochet ecriture-gardien.
      Agent   : lp-mcp-bp
      Ecriture: redirection vers $S/sp_apres.txt
    
  • Arrêt du passage 2 : refus du crochet sur une commande de lecture du journal (awk 'NR>=2486' <journal>). Le > de l'expression awk, entre apostrophes, a été lu comme une redirection. Message exact :
    PreToolUse:Bash hook error: [python -I "$CLAUDE_PROJECT_DIR/Tools/claude/hooks/ecriture-gardien.py"]: Refus du crochet ecriture-gardien.
      Agent   : lp-mcp-bp
      Ecriture: redirection vers =2486'; redirection vers 2489'
    
    La faute était la mienne : la consigne prévenait du crochet. Arrêt immédiat, sans nouvel essai ; reprise sur nouvelle consigne de l'orchestrateur.
  • Lectures hors de la liste de la fiche, sans écriture : list_variables (avant et après add_variable), pour établir que la variable est locale et qu'elle n'existait pas.
  • describe_toolset complet (74 787 caractères) : la fiche le prescrit (rubrique 5), alors que lp-mcp § 1 règle 6 le déconseille. La réponse est partie dans un fichier du harnais, sans charger le contexte.
  • Comportement, point d'attention (estimé, sans écart à la SPEC) :
    • Avant B2-a, la chaîne pure GetSkeletalMesh(SMC) → GetObjectName → Append n'était évaluée que dans le corps de boucle, donc jamais si ClothTable est vide.
    • Depuis B2-a, elle est évaluée une fois, avant la boucle, à chaque appel.
    • Seul cas où l'écart se voit : ClothTable vide et SMC nul. Un appel sur cible nulle (GetSkeletalMesh) pourrait alors écrire un Accessed None qui n'existait pas.
    • Dans le cas courant (ClothTable non vide), une seule évaluation remplace ≈ 615 évaluations.
    • Garde IsValid non ajoutée : ni la SPEC ni la fiche ne la demandent (lp-mcp § 1 règle 4).
    • IsGenitalExposed passe par un cast en SkeletalMeshComponent avant l'appel (SPEC § 2.3, IG:9-68), donc SMC n'est pas nul dans ce chemin (estimé). À confirmer en VAL (§ 5).

3. Instantané

Non relancé, sur consigne de la fiche. Instantané de repli inchangé : D:\Game\LifePlay_Backup_5.5\STE-H10\Content\BPs\GI.uasset (FE4406A0294A8001F612CD03EE2C20CC, manifeste STE-H10, relevé à l'ouverture de STE-H10-GI1). C'est l'état d'avant tout STE-H10 (GI1 + GI2).

Retour arrière proposé, jamais lancé, éditeur fermé et sur décision de l'orchestrateur : .\Tools\Restore.ps1 -Lot STE-H10 -Only Content\BPs\GI.uasset -WhatIf, puis sans -WhatIf. GI revient alors à FE4406A0, ce qui retire aussi GI1. L'état intermédiaire 324a4f27… (GI1 seul) n'est conservé dans aucun instantané.

4. Ce qui n'a pas été fait

  • CompileAllBlueprints ou LPDumpFunction, éditeur fermé : relève de la suite (lp-mcp § 1 règle 3), pas de cette fiche.
  • Build empaqueté et LogStats : hors rôle (lp-mcp § 1 règle 5).
  • Fermeture de l'éditeur, BUILD-FIN : à l'orchestrateur.

5. Points à confirmer en VAL

Graphes, éditeur fermé :

  • -BP=GI -Fn=SavePreset -Pins -Exec → 506 nœuds ; IfThenElse_2957.then → IfThenElse_11 seul ; IfThenElse_11.else → CallFunction_16039 ; IfThenElse_11.then → VariableSet_14 (RevertSkipped = true) ; VariableSet_14.then non relié ; IfThenElse_2957.else non relié.
  • -BP=GI -Fn=GetSMBoolForSMC_Cloth -Pins -Exec → 13 nœuds ; FunctionEntry_2157.then → VariableSet_0 → MacroInstance_1872.Exec ; VariableSet_0.MeshSuffix ← CommutativeAssociativeBinaryOperator_0 ; CallFunction_2.InSuffix ← VariableGet_0 ; MeshSuffix est une variable locale de la fonction, pas une variable membre de GI.

Fichiers et compilation :

  • Content\BPs\GI.uasset = 71034a04c6d64e1a806ca0dadc934fb9 (2026-10-13 00:54:31) ; Person d1b4c63d… et TopRight 1fcb94a6… inchangés.
  • CompileAllBlueprints éditeur fermé : 0 erreur et 0 avertissement nouveaux sur GI.

En jeu :

  • aucun Accessed None nouveau dans GetSMBoolForSMC_Cloth (cas SMC nul avec ClothTable vide, § 2) ;
  • mêmes états « exposé » sur une scène rejouée (SPEC, critères B2-a) ;
  • SavePreset n'appelle plus RevertStripAll quand SaveBeforeDestroy est vrai, et met RevertSkipped à true (B1-a).

6. Fichiers à commiter

Dépôt du jeu, copie 5.8, branche ue5.8 :

  • A Docs/Migration/etat/ST/STE/STE-H10-GI2-IMPL.md (ce livrable).
  • M Content/BPs/GI.uasset (MD5 71034a04c6d64e1a806ca0dadc934fb9, 2026-10-13 00:54:31 ; contient GI1 + GI2), après la VAL.
  • Ni Content/BPs/Person.uasset ni Content/UI/TopRight.uasset : déjà modifiés avant cette fiche, inchangés par elle.
  • Ni Saved/Autosaves/… : hors suivi.

Tours : 35 / 45 (passage 3 ; passage 2 : 15 / 60 ; passage 1 : 24)

Constats

  • add_variable crée une variable locale quand on lui passe graph:{refPath:"/Game/BPs/X.X:<Fonction>"}. On la vérifie par list_variables {blueprint, graph}. Ses accesseurs sortent de find_node_types filtré sur ce graphe (Variables|Pardéfaut|Obtenir<Var> / Définir<Var>) et le nœud créé porte |Get<Var> / |Set<Var>. Le Set a pour broches execute 0, valeur 1, then 0, Output_Get 1.
  • Les toolsets : save_assets est dans editor_toolset.toolsets.asset.AssetTools et rend true ; les outils de graphe sont dans editor_toolset.toolsets.blueprint.BlueprintTools.
  • describe_toolset de BlueprintTools fait 74 787 caractères sur une ligne, que le harnais enregistre sous tool-results\. On en extrait un schéma par grep -o '.\{0,200\}add_variable.\{0,2200\}' <fichier>.
  • Compter une réponse find_nodes sans fichier : elle est dans la transcription de l'agent (subagents\agent-<id>.jsonl). Choisir la ligne par le nombre de correspondances, car la commande Bash elle-même y figure aussi. Comparer deux listes par ( l1 ; l2 ) | sort | uniq -u.
  • Le crochet ecriture-gardien lit tout > comme une redirection, même entre apostrophes (awk 'NR>=N'), ainsi qu'une cible $VAR/…. Pour lire une fin de journal : tail -n +N <fichier> | grep …, ou Read avec offset.
  • Le journal de l'éditeur est horodaté en UTC (22.54 UTC = 00:54 locale, +0200).
  • Les numéros de nœuds sont uniques par graphe, pas par Blueprint (VariableSet_0, VariableGet_0 dans une fonction de GI).