16 KiB
Fiche CAM-B2f (+ amendement B2f-a) — Niveau N3 — Modèle prévu non précisé (CAM-TACHES.md:29, fiche § 2) | utilisé claude-opus-5-5 — Agent lp-mcp-bp — Skills sk-protocol, sk-change-safety, lp-mcp, lp-lot
CAM-B2f-IMPL : Room, sonde C2 de Camera à Visibility par MCP (2026-10-13)
Résultat : réussi après l'amendement B2f-a. Premier passage : arrêt à l'étape 6, car save_assets n'avait rien
écrit (Constats C1 à C5). Reprise B2f-a (fiche § 5) : on a créé puis supprimé un nœud Branche isolé pour marquer
Room comme modifié. Ensuite is_dirty = vrai, relecture conforme, compilation stricte sans exception, et Room
enregistré seul. MD5 de Room CA2A9A0B… → F2B082D2… ; GI et Person sont inchangés (section B2f-a).
1. Fichiers modifiés
Content/BPs/Room.uasset: grapheEventGraph, nœudsK2Node_CallFunction_459,_499et_505. BrocheTraceChannel(entrée, index 4) passée deTraceTypeQuery2àTraceTypeQuery1. Aucun autre changement voulu : le nœud temporaireK2Node_IfThenElse_101a été créé puis supprimé avant l'enregistrement. MD5 avantCA2A9A0BAACA4130FD5C318797F008AE(6 378 087 octets), aprèsF2B082D2911D9F7C7BE60C2CF30EE6D5(6 378 021 octets), enregistré le 2026-10-13 à 19:50:29 (heure locale).Docs/Migration/etat/CAM/CAM-B2f-IMPL.md: ce livrable (nouveau).- Effet de bord, hors dépôt : la sauvegarde automatique de l'éditeur a écrit
Saved/Autosaves/Game/BPs/Room_Auto1.uassetetSaved/Autosaves/PackageRestoreData.jsonà 19:47:39 (heure locale), entrecreate_nodeetdelete_node(section B2f-a, A3).
2. Écarts à la SPEC
- E1 (premier passage, résolu par B2f-a) :
save_assets→truesans rien écrire (Tous les fichiers sont déjà enregistrés.), parce queset_pin_valuene marque pas Room comme modifié (Constats C3). - E2 (non bloquant) : le
type_iddes nœuds estCollision|SphereTraceByChannel, le nom d'affichage deUKismetSystemLibrary::SphereTraceSingle(KismetSystemLibrary.h:1299-1300du moteur 5.8 installé). - E3 (B2f-a, prescrit par l'amendement) : un nœud a été créé puis supprimé, alors que la fiche § 2 disait « aucun nœud
créé ni supprimé ». Le graphe final n'a pas ce nœud :
find_nodesne le liste plus (section B2f-a, A2). - E4 (B2f-a, lectures en plus des étapes) :
get_node_infosdu nœud créé, avant et aprèsdelete_node, etfind_nodespour vérifier sa disparition. Le premierfind_nodesa été refusé par le schéma (titleobligatoire, erreur de paramètre de ma part) et refait une fois avectitle: "", comme le prévoitlp-mcp§ 3. - E5 (B2f-a, effet de bord) : la sauvegarde automatique de l'éditeur dans
Saved/Autosaves(A3). Aucun fichier deContentn'a été écrit par elle (mesuré). hyper_blueprint_debug_inspectest absent du serveur. Repli appliqué :compile_blueprint {warnings_as_errors:true}- journal de l'éditeur.
3. Instantané
- Pris par l'orchestrateur avant tout appel :
.\Tools\Snapshot.ps1 -Lot CAM-B2f. ManifesteD:\Game\LifePlay_Backup_5.5\CAM-B2f\manifest.csv, lu le 2026-10-13 à 19:38 (heure locale) :
"Rel","Existed","Size","MD5","Date"
"Content\BPs\Room.uasset","True","6378087","CA2A9A0BAACA4130FD5C318797F008AE","2026-10-13T19:36:19"
- Contrôle C9a avant écriture :
md5sum Content/BPs/Room.uasset→ca2a9a0baaca4130fd5c318797f008ae(= attendu). - Retour arrière (proposé, non lancé) :
.\Tools\Restore.ps1 -Lot CAM-B2f -WhatIf, puis.\Tools\Restore.ps1 -Lot CAM-B2f -Only Content\BPs\Room.uasset, éditeur fermé, sur accord.
4. Ce qui n'a pas été fait
- Pas de comptage des 1260 nœuds de Room : il revient à la VAL (V5, dump).
- Pas de
CompileAllBlueprints, pas de build, pas de commandlet, pas de jeu (hors fiche). - L'éditeur n'a pas été fermé (pid 28928, toujours ouvert) : c'est à l'orchestrateur, puis
BUILD-FIN. - Je n'ai pas nettoyé
Saved/Autosaves(A3) : c'est signalé ici, pas supprimé.
5. Points à confirmer en VAL
- V5 dump Room (
-BP=Room -Fn=* -Pins -Exec) :TraceTypeQuery1sur 459, 499 et 505, et 1260 nœuds. AucunK2Node_IfThenElse_101dans le dump. - Diff du dump avec celui de l'instantané (
CA2A9A0B) : seules les 3 valeursTraceChannelchangent. La taille du fichier baisse de 66 octets (6 378 087 → 6 378 021). Cause non vérifiée : vignette régénérée à l'enregistrement, sérialisation ou autre. - MD5 de Room =
F2B082D2911D9F7C7BE60C2CF30EE6D5à l'ouverture de la VAL (sinon, quelqu'un a réécrit Room). - V6
CompileAllBlueprints= référence ; V7 dans le build COMMUN-3. - Au prochain lancement de l'éditeur, ne pas accepter la restauration de
Room_Auto1si elle est proposée : c'est un état intermédiaire, qui contient le nœud temporaire (A3).
6. Fichiers à commiter
Dépôt du jeu, D:\Game\LifePlay-master 5.8, branche ue5.8 :
A Docs/Migration/etat/CAM/CAM-B2f-IMPL.mdM Content/BPs/Room.uasset(MD5F2B082D2911D9F7C7BE60C2CF30EE6D5). À confirmer par l'orchestrateur : Room était déjàMavant cette fiche (état git du début de session ; l'instantanéCA2A9A0Bn'est pas la version de HEAD). Le commit de Room portera donc aussi les changements non commités d'avant B2f.Saved/Autosaves/…: rien à commiter (dossier ignoré).
Tours : 51 / 60
Constats
C1. Premier passage : appels MCP dans l'ordre (un par message, journal D:\Game\LifePlay_Backup_5.5\CAM-B2f\editeur\editeur-B2f.log, heures UTC)
| # | Appel | Retour | Journal |
|---|---|---|---|
| 1 | AssetTools.load_asset {asset_path:"/Game/BPs/Room"} |
{"refPath":"/Game/BPs/Room.Room"} |
l. 2243-2244, 17:38:43 |
| 2 | BlueprintTools.get_node_infos (459, 499, 505 groupés) |
3 nœuds, lus en C2 | l. 2251-2252 |
| 3 | set_pin_value {pin:{EGPD_Input, 4, …CallFunction_459}, value:"TraceTypeQuery1"} |
null |
l. 2253-2254 |
| 4 | set_pin_value idem sur …CallFunction_499 |
null |
l. 2255-2256 |
| 5 | set_pin_value idem sur …CallFunction_505 |
null |
l. 2257-2258 |
| 6 | get_node_infos (459, 499, 505 groupés), relecture |
3 nœuds, lus en C2 | l. 2259-2260 |
| 7 | compile_blueprint {blueprint:{refPath:"/Game/BPs/Room.Room"}, warnings_as_errors:true} |
null (pas d'exception) |
l. 2261-2264 |
| 8 | AssetTools.save_assets {asset_paths:["/Game/BPs/Room"]} |
true, mais Tous les fichiers sont déjà enregistrés. |
l. 2265-2267 |
Aux lignes 2234-2245 (17:38:43-44), le client se reconnecte au moment du premier appel : Unknown session id … client should reinitialize, puis Call to unknown method "server/discover". L'appel 1 est passé ensuite sans erreur.
C2. Nœuds touchés : avant et après (refPath /Game/BPs/Room.Room:EventGraph.K2Node_CallFunction_<n>)
| Nœud | Position | TraceChannel (index 4) avant → après |
Radius | bIgnoreSelf | ActorsToIgnore ← | execute ← | then → | Start ← | End ← | OutHit → | ReturnValue → |
|---|---|---|---|---|---|---|---|---|---|---|---|
| 459 | 35750, 1500 | TraceTypeQuery2 → TraceTypeQuery1 |
20.0 | true | MakeArray_5 [0] | CallFunction_494 [0] | IfThenElse_90 [0] | CallFunction_455 [0] | CallFunction_495 [0] | CallFunction_460 [0] | IfThenElse_90 [1] |
| 499 | 31450, 4500 | TraceTypeQuery2 → TraceTypeQuery1 |
20.0 | true | MakeArray_5 [0] | CallFunction_467 [0] | CallFunction_474 [0] | CallFunction_455 [0] | CallFunction_468 [0] | CallFunction_500 [0] | CallFunction_501 [2] |
| 505 | 38450, 1500 | TraceTypeQuery2 → TraceTypeQuery1 |
20.0 | true | MakeArray_5 [0] | IfThenElse_95 [0] | VariableSet_66 [0] | CallFunction_455 [0] | CallFunction_504 [0] | CallFunction_506 [0] | CallFunction_507 [2] |
Sur les 3 nœuds, lors des trois lectures (appels 2, 6 et B2f-a n° 16) : bTraceComplex false, DrawDebugType None,
TraceColor (1,0,0,1), TraceHitColor (0,1,0,1), DrawTime 5.0. Les relectures ne diffèrent de la première
lecture que par TraceChannel.
C3. Cause de E1 (établie dans les sources du moteur 5.8 installé, D:\Game\Launcher\Engine\UE_5.8)
set_pin_value→UBlueprintGraphPinLibrary::SetPinValue(Engine/Source/Editor/BlueprintEditorLibrary/Private/BlueprintEditorLibrary/BlueprintGraphPin.cpp:79) appelleSchema->TrySetDefaultValue(*NativePin, Value, false). Le 3e paramètre estbMarkAsModified,truepar défaut (Engine/Source/Editor/BlueprintGraph/Classes/EdGraphSchema_K2.h:576).save_assets(Engine/Plugins/Experimental/Toolsets/EditorToolset/Content/Python/editor_toolset/toolsets/asset.py:343-349) appelleeas.save_asset(path), donc avecbOnlyIfIsDirty = true(Engine/Source/Editor/UnrealEd/Public/Subsystems/EditorAssetSubsystem.h:296). Un paquet non marqué n'est pas écrit, et l'appel rendtrue.- Dans
asset.pyetblueprint.py, aucun outil ne marque un paquet comme modifié. Seul l'outil de lectureAssetTools.is_dirtyexiste (asset.py:353).
C4. Mesuré à la fin du premier passage
$ md5sum Content/BPs/Room.uasset Content/BPs/GI.uasset Content/BPs/Person.uasset
ca2a9a0baaca4130fd5c318797f008ae *Content/BPs/Room.uasset
e64b5b37c9ef0db7735de0d6e3bd7c1c *Content/BPs/GI.uasset
8668a47c627f7a5254910cf0ac33b12a *Content/BPs/Person.uasset
Fin du journal : [2026.10.13-17.39.53:161][942]LogFileHelpers: Tous les fichiers sont déjà enregistrés.
C5. Options proposées après le premier passage
On a retenu l'option 1 : marquer Room comme modifié dans la même session, avec les outils de la fiche. L'orchestrateur l'a prescrite dans l'amendement B2f-a (fiche § 5).
B2f-a : reprise (amendement, fiche § 5)
A1. Appels MCP dans l'ordre (un par message, même journal, heures UTC)
| # | Appel | Retour | Journal |
|---|---|---|---|
| 9 | create_node {graph:{refPath:"/Game/BPs/Room.Room:EventGraph"}, type_id:"Utilitaires|ContrôleDeFlux|Branche", pos:{x:-20000, y:-20000}} |
{"refPath":"/Game/BPs/Room.Room:EventGraph.K2Node_IfThenElse_101"} |
l. 2272-2273, 17:47:29 |
| 10 | get_node_infos K2Node_IfThenElse_101 |
Branche, position (-20000, -20000), 4 broches (execute, Condition = true, then, else), 0 lien | l. 2274-2275 |
| 11 | delete_node {node:{refPath:"…K2Node_IfThenElse_101"}} |
null |
l. 2286-2287, 17:47:41 |
| 12 | AssetTools.is_dirty {asset_path:"/Game/BPs/Room"} |
true |
l. 2288-2289 |
| 13 | get_node_infos K2Node_IfThenElse_101 |
se résout encore (même contenu) : objet en attente du ramasse-miettes, voir A2 | l. 2290-2291 |
| 14 | find_nodes {graph, node_class:K2Node_IfThenElse} |
refusé : input param "title" is required |
l. 2292-2293 |
| 15 | find_nodes {graph, title:"", node_class:{refPath:"/Script/BlueprintGraph.K2Node_IfThenElse"}} |
liste des Branche du graphe : K2Node_IfThenElse_101 absent, plus grand numéro listé _100 |
l. 2294-2295 |
| 16 | get_node_infos (459, 499, 505 groupés) |
TraceTypeQuery1 sur les 3, reste identique à C2 |
l. 2296-2297 |
| 17 | compile_blueprint {blueprint:{refPath:"/Game/BPs/Room.Room"}, warnings_as_errors:true} |
null (pas d'exception) |
l. 2298-2301 |
| 18 | AssetTools.save_assets {asset_paths:["/Game/BPs/Room"]} |
true, Room écrit |
l. 2305-2338, 17:50:28 |
A2. Nœud temporaire : créé, puis retiré du graphe
- Avant suppression (appel 10), le nœud était bien celui que j'avais créé :
type_idBranche, position (-20000, -20000), aucun lien. - Après
delete_node, il se résout encore par son refPath (appel 13). C'est normal :UEdGraphNode::DestroyNodeappelleParentGraph->RemoveNode(this)avec le commentaire « Will be GC'd after this » (Engine/Source/Runtime/Engine/Private/EdGraph/EdGraphNode.cpp:554-560), etUEdGraph::RemoveNodele retire deNodes(EdGraph.cpp:266). L'objet reste en mémoire jusqu'au ramasse-miettes. find_nodesparcourtbp_editor.list_all_nodes()(blueprint.py:706-709) et ne le liste plus (appel 15).delete_node→UBlueprintGraphEditor::RemoveNodes→FBlueprintEditorUtils::RemoveNode(Blueprint, Node, true)(Engine/Source/Editor/BlueprintEditorLibrary/Private/BlueprintEditorLibrary/BlueprintGraphEditor.cpp:1576-1598).
A3. Sauvegarde automatique de l'éditeur (effet de bord)
À 17:47:39 UTC, entre les appels 10 et 11, l'éditeur a fait sa sauvegarde automatique (l. 2276-2285) :
[2026.10.13-17.47.39:691][ 90]LogFileHelpers: Editor autosave (incl. external actors) for '/Game/Scenes/CentralLondon' took 0.099
[2026.10.13-17.47.39:871][ 90]Cmd: OBJ SAVEPACKAGE PACKAGE="/Game/BPs/Room" FILE="../../../../../../LifePlay-master 5.8/Saved/Autosaves/Game/BPs/Room_Auto1.uasset" SILENT=false AUTOSAVING=true
[2026.10.13-17.47.40:602][ 90]LogFileHelpers: Auto-saving content packages took 0.911
Mesuré juste avant l'appel 18 (19:50:22, heure locale) :
$ find Content -newermt "-20 minutes" -type f
(aucune ligne)
$ find Saved/Autosaves -newermt "-20 minutes" -type f
Saved/Autosaves/Game/BPs/Room_Auto1.uasset
Saved/Autosaves/PackageRestoreData.json
$ md5sum Content/BPs/Room.uasset
ca2a9a0baaca4130fd5c318797f008ae *Content/BPs/Room.uasset
Room_Auto1.uasset contient l'état intermédiaire, avec K2Node_IfThenElse_101 (estimé : la sauvegarde a eu lieu
avant l'appel 11). Je ne l'ai pas supprimé. Je ne sais pas si PackageRestoreData.json a été remis à jour par
l'enregistrement de l'appel 18 : c'est incertain, et la lecture de ce fichier le trancherait.
A4. Enregistrement (appel 18), journal
[2026.10.13-17.50.28:576][ 47]LogFileHelpers: Saving Package: /Game/BPs/Room
[2026.10.13-17.50.28:587][ 47]Cmd: OBJ SAVEPACKAGE PACKAGE="/Game/BPs/Room" FILE="../../../../../../LifePlay-master 5.8/Content/BPs/Room.uasset" SILENT=true
[2026.10.13-17.50.29:171][ 47]LogSavePackage: Moving output files for package: /Game/BPs/Room
[2026.10.13-17.50.29:229][ 48]AssetCheck: /Game/BPs/Room Validation de la ressource
Seul /Game/BPs/Room a été enregistré. Le journal finit à la ligne 2338. On compte 0 ligne Error ou Warning
depuis la ligne 2265 (tail -n +2265 <journal> | grep -c -E "Error|Warning" → 0).
A5. Mesuré après l'appel 18 (19:50:36, heure locale)
$ md5sum Content/BPs/Room.uasset Content/BPs/GI.uasset Content/BPs/Person.uasset
f2b082d2911d9f7c7be60c2cf30ee6d5 *Content/BPs/Room.uasset
e64b5b37c9ef0db7735de0d6e3bd7c1c *Content/BPs/GI.uasset
8668a47c627f7a5254910cf0ac33b12a *Content/BPs/Person.uasset
$ stat -c '%y %s %n' Content/BPs/Room.uasset Content/BPs/GI.uasset Content/BPs/Person.uasset
2026-10-13 19:50:29.168177700 +0200 6378021 Content/BPs/Room.uasset
2026-10-13 19:26:08.639049400 +0200 52636801 Content/BPs/GI.uasset
2026-10-13 17:42:36.059256500 +0200 20821244 Content/BPs/Person.uasset
$ find Content -newermt "-25 minutes" -type f
Content/BPs/GI.uasset
Content/BPs/Room.uasset
- Room : MD5 différent de
ca2a9a0b…(attendu), date 19:50:29, soit l'appel 18. - GI :
e64b5b37…(attendu, inchangé). Il apparaît dans la fenêtre de 25 minutes à cause de sa date, 19:26:08. Elle est antérieure à cette fiche : on la relevait déjà à 19:39, dans C4. - Person :
8668a47c…(attendu, inchangé), date 17:42:36.
À verser : lp-mcp § 3 — set_pin_value ne marque pas le Blueprint comme modifié (BlueprintGraphPin.cpp:79, TrySetDefaultValue(…, false)), et save_assets n'enregistre que les paquets marqués (asset.py:348, EditorAssetSubsystem.h:296). Un true accompagné de « Tous les fichiers sont déjà enregistrés » veut dire que rien n'a été écrit. Le moyen qui marche : create_node d'une Branche isolée, puis delete_node, puis is_dirty = vrai avant save_assets, et contrôle du MD5 après. Autres points : un nœud supprimé se résout encore par refPath jusqu'au ramasse-miettes, c'est find_nodes qui fait foi ; find_nodes exige title ("" pour tous) ; un paquet marqué déclenche la sauvegarde automatique de l'éditeur dans Saved/Autosaves.
Suite : l'orchestrateur doit fermer l'éditeur (pid 28928), faire BUILD-FIN, puis lancer la VAL (V5 dump Room, V6) et le ticket au gardien. État : Room enregistré (F2B082D2…), GI et Person inchangés, éditeur encore ouvert.