38 KiB
Fiche STF-T5a-VAL — Niveau N1 — Modèle prévu sonnet | utilisé claude-sonnet-5-5 — Agent lp-validator — Skills sk-protocol, sk-recount, debug-verification, lp-validate
STF-T5-VAL : VAL statique de la partie (a) de STF-T5, MainMenu SpinBox_0 -> SpinBoxDOF (2026-10-10)
Copie D:\Game\LifePlay-master 5.8, branche ue5.8. Tag STF-T5-avant = tag annoté sur 4074d90 (= HEAD à la relecture ; git rev-parse des deux : 4074d90cecb0229c874ddeca52a5c88ad8c54adb).
Validateur sans éditeur, commandlet, build, MCP ; sans écriture dans Content / Config / Source ; sans git en écriture. Seul fichier écrit : ce livrable (LF, comme le dossier
etat/ST). Partie (b) (CharGen) : hors de cette VAL, bloquée par la fiche. Heures des journaux d'éditeur en UTC (local = UTC + 2 h).
Méthode : avant = git show STF-T5-avant:Content/UI/MainMenu.uasset (MD5 00e42d72c773a55f6998ae67662fdb76, 2 246 412 o), après = fichier de la copie (MD5
7292c6beb26a8066343abddf49b63d9f, 2 260 418 o, copié dans le scratchpad hors dépôt). Lecture par Tools/uasset58.py (locate, events, props, python -I) et par mes scripts du
scratchpad (entry.py, diffexp2.py, t5.py, edges2.py, props.py). Contrôle d'intégrité du décodeur : exports contigus dans les deux états (affiché par uasset58.py : contigus=True,
1722 puis 1727 exports ; tables de noms 1219 puis 1224).
1. Fiches amont relues
STF-T5-CART.md (§ 1-3), STF-T5-SPEC.md (§ 0-5, partie (a) = § 1, critères W1-W6 et J = § 4), STF-T5-IMPL.md (§ 1-5). Relu aussi pour le contexte : mes STF-VAL.md (STF-T4) et
STF-SPEC.md § 7. Formes (gabarits lp-doc) : STF-T5-IMPL.md a un en-tête et un titre, mais ni les sections IMPL numérotées (1 fichiers modifiés, 2 écarts, 3 instantané, 4 non fait, 5 points à
confirmer, 6 fichiers à commiter A / M / D), ni la ligne Tours ; STF-T5-CART.md n'a pas les sections CART 4-6 ni Tours. Sans effet sur le fond.
2. Points à confirmer des IMPL
STF-T5-IMPL.md n'a pas de section « points à confirmer » ; je relève ce qu'il déclare ou renvoie à la VAL.
| # | Point (source) | Verdict | Preuve |
|---|---|---|---|
| P1 | MD5 avant / après MainMenu ; CharGen et GI inchangés (IMPL tableau) | PASS (CharGen 9669b103… = valeur T5-T0 / IMPL ; GI 606eb437… = celle de STF-T5-avant) |
§ 3 C0 |
| P2 | SpinBoxDOF : 5 occurrences, Get_SpinBox_0_Value_0 : 0 (IMPL § 2) |
PASS | C1 |
| P3 | Événements : 47 (OnClicked 32, OnValueCommitted 14, OnValueChanged 1), SpinBox_0 absent (IMPL § 2) |
PASS | C2 |
| P4 | Écart de méthode : lecteurs purs (VariableGet_20, _21, GetArrayItem_3, VariableGet_27, _28) partagés entre OnValueCommitted et Construct (IMPL § 1) |
PASS-STATIC (graphe lu au niveau nœud : mêmes nœuds cités des deux côtés) | C3, C6 |
| P5 | Sorties « faux » : OnValueCommitted non branchées ; Construct : les trois rejoignent UpdateDonors (IMPL § 1) |
PASS-STATIC (niveau nœud) | C6 |
| P6 | Nœuds orphelins : aucun (IMPL § 1) | PASS-STATIC : aucun export orphelin additionnel dans le diff (C3) ; les 22 exports ajoutés sont tous cités par la chaîne (C6). Un nœud pur sans lien serait invisible au niveau nœud | C3, C6 |
| P7 | PIE : champ = 2 (1 × 2), saisie 4 -> DepthOfFieldFarBlurSize 2 (IMPL § 3 W3 / W4) |
reporté : relevés MCP de l'orchestrateur (établi par lui), non rejouables sans éditeur ; valeurs de GI absentes du journal | C7 |
| P8 | Journal PIE : Get_SpinBox_0_Value_0 0, MainMenu_C 0, 4 PIE: Error hors périmètre (IMPL § 3) |
PASS (recompté) | C7 |
| P9 | W1 : 418 / 418, Success - 0 error(s), 0 warning(s), MainMenu l. 2802 (IMPL § 4) |
PASS (recompté) | C5 |
| P10 | Suppression de la fonction sans autre référence (IMPL § 1) : 5 assets référencent MainMenu | reporté : relevé find_nodes / grep de l'orchestrateur non rejoué ; le diff d'exports montre que seuls le widget, ComponentBoundEvent_19 et le graphe Get_SpinBox_0_Value_0 ont disparu (C3) |
|
| P11 | Changement de comportement à accepter (SPEC § 1, fin) : le champ ne suit plus le flou en continu | à confirmer par l'utilisateur (case 3, § 6) | — |
3. Critères rejoués (commande -> compte brut)
C0 — État de la copie : PASS
| Commande | Résultat brut |
|---|---|
git rev-parse "STF-T5-avant^{commit}" ; git rev-parse HEAD |
4074d90cecb0229c874ddeca52a5c88ad8c54adb pour les deux ; git cat-file -t STF-T5-avant = tag |
git status --short -- Content Config Source |
2 lignes : M Content/UI/CharGen.uasset, M Content/UI/MainMenu.uasset |
md5sum Content/UI/MainMenu.uasset |
7292c6beb26a8066343abddf49b63d9f (attendu) |
git show STF-T5-avant:Content/UI/MainMenu.uasset | md5sum |
00e42d72c773a55f6998ae67662fdb76 (attendu ; = instantané manifest.csv 00E42D72…) |
md5sum Content/BPs/GI.uasset ; git show STF-T5-avant:Content/BPs/GI.uasset | md5sum |
606eb43714c1f4317300b797edd02e62 ; 606eb43714c1f4317300b797edd02e62 : identiques |
md5sum Content/UI/CharGen.uasset |
9669b103d0a056f0bd9089936d848963 : la modification de CharGen (git diff --stat : 5 028 080 -> 5 047 338 o) est celle de STE-TXT (même MD5 que T5-T0 et IMPL), pas de cette partie (a) |
git diff --stat STF-T5-avant -- Content Config Source |
2 fichiers binaires : CharGen.uasset (STE-TXT, hors lot) et MainMenu.uasset 2 246 412 -> 2 260 418 o ; rien dans Config / Source |
md5sum Saved/SaveGames/*.sav |
GameSettings.sav 590b2cc503344f5b76971517b1de098e, Default.sav 81f13c39eaf5dde64981fae83b1ef70a = manifest.csv de STF-T5 (590B2CC5…, 81F13C39…) : W6 PASS |
C1 — W2, liaisons : PASS (établi sur fichier ; asset non chargé par le moteur)
python -I Tools/uasset58.py locate <asset> SpinBox_0 SpinBoxDOF Get_SpinBox_0_Value_0 SpinBoxSexDuration SliderCameraSpeed :
| Nom | Avant | Après |
|---|---|---|
SpinBox_0 |
6 (2 exports SpinBox dans WidgetTree, 2 dans MainMenu WidgetBlueprint à +8026 et +47583, 2 dans MainMenu_C à +5829 et +15863) |
0 |
SpinBoxDOF |
0 | 5 (table des noms + 2 exports SpinBox + 1 dans MainMenu +46415 + 1 dans MainMenu_C +15328) |
SpinBoxSexDuration (témoin sans liaison) |
5 | 5 |
SliderCameraSpeed (témoin sans liaison) |
5 | 5 |
Get_SpinBox_0_Value_0 (chaîne entière) |
0 | 0 |
Remarque : l'attendu « 0 après » est vrai mais ne distingue pas l'avant, car le nom s'écrit en table sous la forme FName « Get_SpinBox_0_Value » + suffixe 1 (la chaîne …_Value_0 n'existe dans aucun des deux |
||
états). La preuve utile est la table des noms (python t5.py) : le nom Get_SpinBox_0_Value est dans la liste des noms retirés (avec BndEvt__SpinBox_0_K2Node_ComponentBoundEvent_19_… et |
||
K2Node_FunctionResult_ReturnValue_ImplicitCast) ; il n'existe plus en table après. |
||
Profil de SpinBoxDOF = celui des widgets sans liaison (5 = table + 2 exports + 1 + 1 ; une liaison en ajouterait 2) : aucune liaison sur SpinBoxDOF. |
||
Tableaux de liaisons (python entry.py <asset> MainMenu 0 12000 et … MainMenu_C 0 9000 : FString ObjectName suivie de la balise PropertyName) : |
||
| Tableau | Avant | Après |
| --- | --- | --- |
éditeur (MainMenu) |
19 | 18 |
exécution (MainMenu_C) |
19 | 18 |
Après : plus aucune entrée SpinBox dans les deux tableaux (le filtre SpinBox ne retourne rien). 19 - 1 = 18 : attendu confirmé. |
C2 — Événements : PASS-STATIC
python -I Tools/uasset58.py events <asset> :
| Avant | Après | |
|---|---|---|
nœuds ComponentBoundEvent |
47 | 47 |
OnClicked |
32 | 32 |
OnValueCommitted |
14 (dont _19 = SpinBox_0) |
14 (dont _6 = SpinBoxDOF, export #1047) |
OnValueChanged |
1 (_30 SliderCameraSpeed) |
1 (_30) |
Les 13 autres OnValueCommitted (_31, _32-_37, _39, _40, _43-_46) sont inchangés. _19 (avant) est rattaché à SpinBox_0 par son ComponentPropertyName (FName « SpinBox » + suffixe, que le filtre |
||
de l'outil n'affiche pas) ; _6 (après) porte SpinBoxDOF. Bruit : dans l'état après, l'outil ajoute le widget « Version » à tous les OnValueCommitted (le décalage de 5 indices de noms fait coïncider un indice avec |
||
| une valeur répétée) : faux positif, sans lien avec les widgets. |
C3 — Diff des exports avant / après : PASS, avec écarts d'annonce (ci-dessous)
python diffexp2.py (clé = chemin d'imbrication + nom + classe ; 1722 clés distinctes avant, 1727 après, aucun doublon) : 17 retirés, 22 ajoutés, net +5 = 1727 - 1722.
| Retirés (17) | Ajoutés (22) |
|---|---|
2 SpinBox SpinBox_0 (WidgetTree de MainMenu et de MainMenu_C) |
2 SpinBox SpinBoxDOF |
K2Node_ComponentBoundEvent_19 |
K2Node_ComponentBoundEvent_6 |
EdGraph Get_SpinBox_0_Value_0 + Function Get_SpinBox_0_Value_0 (classe) |
— |
ses 9 nœuds : FunctionEntry_0, FunctionResult_0, BreakStruct_0, CommutativeAssociativeBinaryOperator_0, GetArrayItem_0, VariableGet_0 à _3 |
— |
Function BndEvt__SpinBox_0_K2Node_ComponentBoundEvent_19_… |
Function BndEvt__MainMenu_SpinBoxDOF_K2Node_ComponentBoundEvent_6_… |
2 HorizontalBoxSlot_1 sous HorizontalBox_34 |
2 HorizontalBoxSlot_0 sous HorizontalBox_34 |
| — | K2Node_IfThenElse_10 à _15 (6) |
| — | K2Node_CallFunction_93 à _97 (5) |
| — | K2Node_CallArrayFunction_3, _4 ; K2Node_BreakStruct_1 ; K2Node_PromotableOperator_0 ; K2Node_VariableGet_83 |
Conforme aux attendus de la fiche : tous les ajouts attendus (SpinBoxDOF, ComponentBoundEvent_6, IfThenElse_10 à _15, CallFunction_93 à _97, CallArrayFunction_3 et _4, BreakStruct_1, |
|
PromotableOperator_0, VariableGet_83) sont présents ; tous les retraits attendus (widget, ComponentBoundEvent_19, graphe et nœuds de Get_SpinBox_0_Value_0) sont présents. |
|
| Autres changements à signaler (rien d'inattendu dans les nœuds de jeu) : |
- Slots de
HorizontalBox_34: le slot par défaut de 71 octets de l'enfant SpinBox est renomméHorizontalBoxSlot_1->HorizontalBoxSlot_0(×2, même taille 71 o) ; l'autre slot (HorizontalBoxSlot_3, 300 o, celui du libelléTextBlock_14) est inchangé. Nom d'objet auto-attribué parAddWidget; sans effet. L'indice de l'enfant (1) n'est pas relu par ce décodage (relevé MCP de l'orchestrateur). - Fonction
BndEvt__: remplacée comme attendu (le nom contient désormaisMainMenu_etSpinBoxDOF). - 9 exports communs changent de taille (
diffexp2.py, « COMMUNS dont la taille change » : 9), tous expliqués par l'arithmétique des liens (une liaison de broche = 24 octets) sauf deux résidus :Export Avant -> après Explication EventGraph1957 -> 2021 (+64) 17 nœuds ajoutés - 1 retiré = +16 références x 4 o K2Node_VariableGet_20/_21/_27+48 chacun 2 nouveaux liens chacun ( CallFunction_93+_95;CallArrayFunction_3+_4;CallFunction_94+_96)K2Node_VariableGet_28+24 1 nouveau lien ( BreakStruct_1)K2Node_CallFunction_1278(UpdateDonors)989 -> 1061 (+72) entrée d'exécution : 1 source ( _92) -> 4 sources (_97+ 3 sorties « faux ») = +3 liensExecuteUbergraph_MainMenu31 184 -> 32 889 (+1 705) code d'exécution du graphe d'événements (non décodé) MainMenu(WidgetBlueprint)93 899 -> 93 187 (-712) -562 (entrée de liaison éditeur retirée) ; résidu -150 non attribué MainMenu_C(classe)27 866 -> 27 448 (-418) -403 (entrée de liaison exécution retirée) ; résidu -15 non attribué Les résidus (-150, -15) ne sont pas expliqués à l'octet (estimé : références à la fonction supprimée et au widget renommé dans les tables de ces deux exports) ; ils n'ont pas de contrepartie dans les nœuds ni dans les propriétés relues. Incertain : aucun essai ne tranche sans éditeur. - Le reste des exports (1722 - 9 - 17 retirés = 1696 exports communs inchangés en taille) ne change pas de taille. Les octets d'un export commun ne sont pas comparés (les indices de noms et d'exports ont glissé de +5 / +5).
C4 — Valeurs de SpinBoxDOF : PASS-STATIC (établi sur fichier)
python -I Tools/uasset58.py props <après> SpinBoxDOF et python edges2.py (export #1493 et copie #1507, identiques ; propriétés stockées en delta par rapport au défaut du moteur) :
| Propriété | Dans l'asset | Valeur |
|---|---|---|
MaxValue |
présente, float 0x41a00000 |
20.0 |
MaxSliderValue |
présente, float 0x41a00000 |
20.0 |
bOverride_MinValue / MaxValue / MinSliderValue / MaxSliderValue |
présentes, drapeau 0x10 |
vrai (x4) |
MinValue, MinSliderValue |
absentes (défaut moteur) | 0 |
Value |
absente (défaut moteur) | 0 (valeur de conception de la SPEC) |
Delta, MinFractionalDigits, MaxFractionalDigits, bAlwaysUsesDeltaSnap |
absentes | défauts moteur (0, 1, 6, faux) : conforme à la SPEC (Delta 0, décimales 1 / 6 ; pas d'arrondi entier, E3 de STF ne s'applique pas à ce widget) |
| Min 0 / Max 20, glisser 0-20 : conforme. Le style, la police (Roboto Bold 20) et le slot ne sont pas relus octet à octet (IMPL : « 34 propriétés, 0 écart » : reporté, non rejoué). | ||
B = 2 sur PromotableOperator_0 : la chaîne 2.0 apparaît une fois dans l'export (+680, valeur par défaut d'une broche) ; le nom de la broche porteuse (B) est un FName, non résolu par ce décodage : |
||
estimé (une seule constante 2.0 dans un opérateur « Multiplier » dont l'autre entrée est reliée à BreakStruct_1) ; la classe PromotableOperator et le nom Multiply sont présents (noms ajoutés : Multiply, |
||
OperationName, K2Node_PromotableOperator). |
C5 — W1, CompileAllBlueprints : PASS
Journal Saved\Logs\portage\STF_CompileAllBlueprints_20261010-135203.log (482 232 o ; ouvert 13:52:04 locale, soit après la sauvegarde de l'asset à 13:45 : il compile bien 7292c6be…) :
| Commande | Compte brut |
|---|---|
grep -c 'Loading and Compiling' |
418 |
grep -c 'a réussi' |
418 |
grep -c 'échoué' |
0 |
grep -c 'Error:' |
0 |
grep -n 'Compiling Completed' |
l. 3235 : Compiling Completed with 0 errors and 0 warnings and 0 blueprints that failed to load. |
grep -n 'Success -' |
l. 3242 : LogInit: Display: Success - 0 error(s), 0 warning(s) |
| lignes MainMenu | l. 2800 Loading and Compiling: '/Game/UI/MainMenu.MainMenu' ; l. 2802 [0125,84] La compilation de MainMenu a réussi ! [en 480 ms] |
IMPL § 4 (418 / 418, Success, MainMenu l. 2802) confirmé. Exécution du commandlet par l'orchestrateur : non rejouée (interdit ici). |
C6 — Chaînes de nœuds (écriture et Construct) : PASS-STATIC (niveau nœud, estimé)
python edges2.py (références croisées nœud à nœud : A cite B et B cite A ; sans lecture des broches) : 30 liens attendus, 30 présents.
OnValueCommitted(12) :ComponentBoundEvent_6 -> IfThenElse_10(CallFunction_93<-VariableGet_20)-> IfThenElse_11(CallArrayFunction_3<-VariableGet_21)-> IfThenElse_12(CallFunction_94<-VariableGet_27)-> SetFieldsInStruct_0 -> VariableSet_13;ComponentBoundEvent_6 <-> CommutativeAssociativeBinaryOperator_9(InValuex 0.5).ComponentBoundEvent_6n'est pas lié directement àVariableSet_13.Construct(18) :CallFunction_92 -> IfThenElse_13(CallFunction_95<-VariableGet_20)-> IfThenElse_14(CallArrayFunction_4<-VariableGet_21)-> IfThenElse_15(CallFunction_96<-VariableGet_27)-> CallFunction_97(SpinBoxDOF.SetValue:VariableGet_83,PromotableOperator_0<-BreakStruct_1<-VariableGet_28)-> CallFunction_1278; les 3IfThenElse_13/_14/_15 <-> CallFunction_1278(sorties « faux »). L'ancien lienCallFunction_92 <-> CallFunction_1278: absent (False). Conforme à SPEC § 1.2-1.3 et à IMPL § 1 au niveau nœud. Non établi sans éditeur : lequel des deux pins (vrai / faux) de chaqueIfThenElseporte chaque lien, la valeur littérale 0 de l'index deIsValidIndex, le facteur 0.5 conservé (la constante deCommutativeAssociativeBinaryOperator_9n'est pas relue), la cible exacte deSetValue(self).
C7 — Journal PIE STF-T5a_Editor_20261010-133950.log depuis « Created PIE world » : PASS
Fichier 362 456 o, 2709 lignes, ouvert 13:39:50 locale ; sauvegarde de MainMenu Saving Package: /Game/UI/MainMenu l. 2414 (11:45:50 UTC), avant le PIE.
Commande (à partir de la ligne 2457, tail -n +2457) |
Compte brut |
|---|---|
grep -n 'Created PIE world' |
1 occurrence, l. 2457 (11:48:04.315 UTC) |
grep -c 'Get_SpinBox_0_Value_0' |
0 (0 aussi dans tout le journal) |
grep -c 'MainMenu_C' |
0 |
grep -c 'SpinBox_0' / 'SpinBoxDOF' |
0 / 0 |
grep -c 'PIE: Error' |
4 |
grep -c 'LogScript: Warning' |
4 (les mêmes accès) |
grep -n 'Shutting down PIE' |
l. 2698 (11:51:30.977 UTC) ; Fatal error / Assertion failed / Unhandled : 0 ; pas de LogExit dans le journal (éditeur encore ouvert à la dernière ligne) |
Les 4 PIE: Error restants, tous à 11:48:04.584 UTC (l. 135, 139, 143, 147 depuis la ligne 2457) : |
Accès à "Aucun" … la propriété StreetMapStatic dans /Game/BPs/GI.GI_C— nœud Branche,worksHere, BlueprintPerson…;Accès à None, lecture de la structure Amenity— Branche,worksHere;Accès à "Aucun" … StreetMapStatic dans /Game/BPs/GI.GI_C— nœud retour,worksHere;Accès à None, lecture de la structure Amenity— nœud retour,worksHere. Aucune ne concerneMainMenuniSpinBoxDOF: hors périmètre (carte de rues absente), conformes à IMPL § 3 (« 4PIE: Errorhors périmètre »). Avant (T4 de STF) : 12 erreursMainMenu_Cet 60 lignesGet_SpinBox_0_Value_0; le gain se confirme. Limite : un journal sans erreur ne prouve pas que le champ affiche 2 ni que 4 -> 2 : ces relevés (W3, W4) sont des relevés MCP de l'orchestrateur, non rejouables ici.
4. Diff lu en entier
git diff --stat STF-T5-avant -- Content Config Source : 2 fichiers binaires, dont MainMenu.uasset (partie (a)) et CharGen.uasset (STE-TXT, hors lot). Aucun diff texte à lire : le « diff » de (a) est le diff d'exports de C3
(17 retirés / 22 ajoutés / 9 tailles communes modifiées, dont 2 résidus non attribués) complété par C1, C2, C4 et C6. Fins de ligne : sans objet pour un .uasset ; ce livrable est en LF.
5. Verdict
Verdict global : PASS-STATIC pour la partie (a) (aucun FAIL ; deux réserves ci-dessous). Par contrôle :
- C0 état et W6 fichiers : PASS
- C1 W2 liaisons : PASS (18 / 18 entrées, plus de liaison,
SpinBoxDOFsans liaison) - C2 événements : PASS-STATIC (47 / 47, 14 / 14, 1)
- C3 diff d'exports : PASS (attendus tous présents ; slots renommés et deux résidus de taille signalés)
- C4 valeurs de
SpinBoxDOF: PASS-STATIC (Min 0, Max 20, glisser 0-20, Value 0 ; B = 2 estimé) - C5 W1 : PASS (418 / 418, 0 / 0)
- C6 chaînes : PASS-STATIC (30 / 30 au niveau nœud)
- C7 journal PIE : PASS (0 / 0, 4 erreurs hors périmètre)
- W3, W4 (champ = 2, saisie 4 -> 2, réouverture) : établi par l'orchestrateur, non rejouable ; J (archive Development) : à faire par l'utilisateur.
Réserves : (1) résidus de taille -150 (
MainMenu) et -15 (MainMenu_C) non expliqués à l'octet ; (2) équivalence de comportement lue au niveau nœud, pas broche à broche. Niveau de preuve : décodage du fichier établi (exports contigus) ; équivalence de comportement estimée.
6. Points à confirmer par l'utilisateur
- 1. Accepter le changement de comportement de SPEC § 1 : le champ « Depth of Field Strength » n'est plus réévalué à chaque image ; il affiche la valeur lue à l'ouverture du menu (
Construct) et celle que le joueur valide. - 2. Accepter l'écart de méthode IMPL § 1 (lecteurs purs partagés entre
OnValueCommittedetConstruct) : sans effet attendu, un nœud pur est réévalué à chaque usage. - 3. Preuve en jeu J (archive Development) : le champ se règle, flou désactivé à 0, 0 occurrence de
Get_SpinBox_0_Value_0au journal. Non faite. - 4. Faire ranger les scripts du scratchpad dans
Saved/STF/ouTools/s'ils doivent être rejoués (ils réutilisentTools/uasset58.pypourlocate,events,props). - 5. Corriger les sections manquantes des gabarits (IMPL : 3-6 et
Tours; CART : 4-6 etTours) — hors périmètre du validateur.
Tours : 9 / 20
Constats
CharGen.uassetest modifié dans l'arbre de travail (M, STE-TXT, MD59669b103…stable depuis T5-T0) : ne pas l'inclure dans le commit de la partie (a) (ticket STF-R3,MainMenu.uassetseul).- Le décodeur compare des tailles d'exports et des références croisées ; il ne lit pas les broches. Pour trancher les deux résidus de taille et les liens vrai / faux des
IfThenElse, il faudrait l'éditeur (get_node_infos) : proposition d'essai rejouable par l'orchestrateur, non faite ici. - Aucune nouveauté non demandée vue en passant.
7. Décisions sur § 6 (orchestrateur, 2026-10-10)
- Changement de comportement (affichage lu à l'ouverture du menu) et méthode : déjà acceptés par l'utilisateur (« (a) approuvée » sur STF-T5-SPEC § 1). Écart de méthode (lecteurs purs partagés,
showPinForProperties) : sans effet sur le comportement, consigné dans STF-T5-IMPL § 1. - Commit de (a) avant la preuve en jeu : décision de l'utilisateur (« Maintenant ») ; J de T5 sur la prochaine archive, avec (b).
- Scripts d'analyse de cette passe : jetables (l'outil réutilisable est
Tools/uasset58.py, rangé avec STF). - Gabarits de T5-CART / T5-IMPL : sections non applicables omises (lecture seule / IMPL MCP), sans effet sur les preuves.
- Résidus de taille inexpliqués (−150 sur
MainMenu, −15 surMainMenu_C) et B = 2 estimé : acceptés (PIE W4 : 4 tapé → flou 2, et champ 2 pour un flou 1, établis par relevé MCP).
Partie (b) CharGen
Fiche STF-T5b-VAL — Niveau N1 — Modèle prévu sonnet | utilisé claude-sonnet-5-5 — Agent lp-validator — Skills sk-protocol, sk-recount, debug-verification, lp-validate
VAL statique de la partie (b) de STF-T5 (CharGen, SpinBox_0 « Counteract Vagina Deform »), ajoutée le 2026-10-10 sans toucher à la partie (a) ci-dessus. Mêmes interdits (aucun éditeur, commandlet, MCP, git en écriture ;
seul ce livrable est écrit, en LF). STF-T5b-avant = tag annoté (git cat-file -t : tag) sur 073f9dfe2466070fdb68f3bcc089266ba1378a66 (STE-R3) ; HEAD à la relecture : e3df8ca (OBP-T2, sans effet sur Content).
Méthode : avant = git show STF-T5b-avant:Content/UI/CharGen.uasset (MD5 a0de37acc2cfcb02ef254af2517e4e20, 5 046 138 o) ; après = fichier de la copie (MD5 e4c2eea6a3a2129eb8b382715609b1d5, 5 054 038 o, 15:30 locale) ;
Tools/uasset58.py (locate, events, python -I) et scripts du scratchpad (diffexp2.py, cg.py, cg2.py, entry.py). Exports contigus dans les deux états (3582 puis 3586 exports ; tables de noms 1600 puis 1602).
b1. Fiches amont relues
STF-T5-SPEC.md § 2 (partie (b) : garde seule, OnValueChanged conservé, effet en direct voulu), STF-T5-IMPL.md § 6 (en entier), STF-T5-CART.md § 2 (déjà relu en (a)).
Écart de l'IMPL à la SPEC : Delta 0 -> 0.5, bAlwaysUsesDeltaSnap vrai, décimales 1 / 1 : ajout décidé par l'utilisateur pendant le PIE (IMPL § 6), absent de la SPEC § 2 ; il est contrôlé en C2b.
b2. Points à confirmer de l'IMPL § 6
| # | Point (source) | Verdict | Preuve |
|---|---|---|---|
| Q1 | MD5 avant / après CharGen ; MainMenu, GI, StatMenu inchangés (IMPL tableau) | PASS | C0b |
| Q2 | Garde ComponentBoundEvent_85 -> IfThenElse_37 (IsValid GI) -> IfThenElse_38 (IsValid CharacterBeingEdited) -> CallFunction_303 SetMorphTarget, sorties « faux » non branchées |
PASS-STATIC (niveau nœud) | C1b, C4b |
| Q3 | OnValueChanged gardé (pas de passage à OnValueCommitted) |
PASS : 15 OnValueChanged avant et après |
C3b |
| Q4 | Delta 0.5, bAlwaysUsesDeltaSnap, décimales 1 / 1, bornes inchangées |
PASS-STATIC (établi sur fichier) | C2b |
| Q5 | « Aucun doublon, IfThenElse_39 inexistant » (incident « session expired ») |
PASS : exactement 2 IfThenElse ajoutés (_37, _38), aucun _39 |
C1b |
| Q6 | W1 : 418 / 418, Success - 0 error(s), 0 warning(s), CharGen l. 2800 |
PASS (recompté) | C5b |
| Q7 | PIE : property GI in /Game/UI/CharGen 0 ; restent 4 worksHere + 1 PopulateGenitalSlidersDaz |
PASS (recompté) | C6b |
| Q8 | Effet en direct conservé pendant le glisser (observé par l'utilisateur) | déclaré par l'utilisateur, non rejouable ici | — |
| Q9 | Pas de 0.5 : « non rejoué en PIE (réglé après) » | confirmé par le journal : 2e sauvegarde de CharGen à 13:30:28 UTC, après l'arrêt du PIE (13:30:04) ; établi par relecture des propriétés (C2b) | C2b, C6b |
| Q10 | .sav inchangés, aucun préréglage créé |
PASS pour les .sav ; « aucun préréglage » : non rejoué (fichiers hors Saved/SaveGames) |
C0b |
b3. Critères rejoués (commande -> compte brut)
C0b — État des fichiers : PASS
| Commande | Résultat brut |
|---|---|
git cat-file -t STF-T5b-avant ; git rev-parse "STF-T5b-avant^{commit}" |
tag ; 073f9dfe2466070fdb68f3bcc089266ba1378a66 |
git status --short -- Content Config Source |
1 ligne : M Content/UI/CharGen.uasset |
git diff --stat STF-T5b-avant -- Content Config Source |
`Content/UI/CharGen.uasset |
git show STF-T5b-avant:Content/UI/CharGen.uasset | md5sum ; md5sum Content/UI/CharGen.uasset |
a0de37acc2cfcb02ef254af2517e4e20 (= instantané STF-T5b\manifest.csv) ; e4c2eea6a3a2129eb8b382715609b1d5 (attendu) |
md5sum Content/UI/MainMenu.uasset |
7292c6beb26a8066343abddf49b63d9f (attendu) |
md5sum Content/BPs/GI.uasset |
606eb43714c1f4317300b797edd02e62 (attendu) |
md5sum Content/UI/StatMenu.uasset |
9030531ccf4ef46c133722dacd6d2203 (préfixe 9030531c attendu) |
md5sum Saved/SaveGames/GameSettings.sav ; Default.sav |
590b2cc503344f5b76971517b1de098e ; 81f13c39eaf5dde64981fae83b1ef70a = manifest.csv de STF-T5b (590B2CC5…, 81F13C39…) |
C1b — Diff des exports : PASS
python diffexp2.py CharGen_avant.uasset CharGen_apres.uasset (clé = chemin d'imbrication + nom + classe ; 3582 clés avant, 3586 après, 0 doublon) : 1 retiré, 5 ajoutés, net +4 = 3586 - 3582.
| Ajoutés (attendus) | Statut |
|---|---|
K2Node_IfThenElse_37 et _38 (CharGen/EventGraph) |
présents |
K2Node_CallFunction_59 et _93 (CharGen/EventGraph, IsValid) |
présents |
ComponentDelegateBinding_3 (CharGen_C), en remplacement de ComponentDelegateBinding_1 (retiré) |
non annoncé : objet régénéré à la compilation (même classe, même parent, un retiré / un ajouté) ; sans effet |
Aucun autre ajout ni retrait. K2Node_IfThenElse_37 existe aussi dans le graphe GetVisibility_0 (même nom, autre chemin) : sans lien avec le nouveau nœud, mais à ne pas confondre dans une recherche par nom seul (mon premier test, par nom, a donné un faux négatif avant correction). |
|
Changements de taille des exports communs : 6 (diffexp2.py) : |
|
| Export | Avant -> après |
| --- | --- |
EdGraph EventGraph |
5445 -> 5461 (+16) |
Function ExecuteUbergraph_CharGen |
102 920 -> 103 201 (+281) |
K2Node_VariableGet_280 |
769 -> 793 (+24) |
K2Node_VariableGet_281 |
854 -> 878 (+24) |
SpinBox SpinBox_0 (CharGen et CharGen_C, 2 exports) |
171 -> 254 (+83 chacun) |
Écarts à l'annonce : (i) ComponentBoundEvent_85 et CallFunction_303 sont cités dans la fiche comme changeant de taille ; leur taille n'a pas changé (un lien remplacé par un lien : l'événement pointait sur CallFunction_303, il pointe sur IfThenElse_37 ; CallFunction_303 est |
|
alimenté par IfThenElse_38 au lieu de l'événement) : cohérent, pas un écart de contenu ; (ii) SpinBox_0 (+83 octets x 2) change de taille : attendu par C2b, non listé dans la fiche. Aucun autre export (3586 - 4 ajoutés - 6 = 3576 communs inchangés en taille). |
|
| Résidu d'en-tête non attribué : l'en-tête passe de 2 240 841 à 2 244 168 (+3 327) ; 2 noms (57 o) + 4 exports (448 o) n'expliquent qu'une partie ; le reste (tables de dépendances de pré-chargement) n'est pas décodé : incertain, sans contrepartie dans les nœuds. |
C2b — Propriétés de SpinBox_0 : PASS-STATIC (établi sur fichier)
python cg.py (propriétés stockées en delta par rapport au défaut moteur ; mêmes valeurs dans les deux exports de chaque état) :
| Propriété | Avant | Après |
|---|---|---|
Delta |
absente (défaut 0) | présente, float 0x3f000000 = 0.5 |
bAlwaysUsesDeltaSnap |
absente (nom absent de la table) | présente, drapeau 0x10 = vrai |
MaxFractionalDigits |
absente (défaut 6) | présente, entier 1 (nom ajouté à la table) |
MinFractionalDigits |
absente (défaut 1) | absente (défaut 1) : le nom n'est dans la table ni avant ni après, la valeur 1 = défaut moteur n'est jamais écrite ; résultat 1 / 1 |
MinValue |
absente (défaut 0) | absente (0) |
MaxValue |
absente (non surchargée, bOverride_MaxValue absent) |
absente (idem) |
MaxSliderValue |
présente, float 0x42c80000 = 100.0 |
présente, 100.0 |
MinSliderValue |
absente (0) | absente (0) |
bOverride_MinValue / bOverride_MinSliderValue / bOverride_MaxSliderValue |
présentes, vrai | présentes, vrai |
| Conforme à IMPL § 6 et à la demande : Delta 0.5, snap vrai, décimales 1 / 1 ; Min 0, glisser 0-100 (MaxSlider 100), Max non surchargé : inchangés. Les +83 octets = 29 + 25 + 29, sans autre propriété modifiée dans le widget. | ||
Noms ajoutés à la table (1600 -> 1602) : bAlwaysUsesDeltaSnap, MaxFractionalDigits ; Delta y était déjà (aucun nom retiré). |
C3b — Liaisons et événements : PASS
| Contrôle | Avant | Après |
|---|---|---|
uasset58.py locate : SpinBox_0 |
2 (tableaux CharGen +65038 et CharGen_C +23393 seulement) |
2 (mêmes positions relatives +65038 / +23393) |
uasset58.py locate : TextBlock_69 (témoin sans liaison) |
4 | 4 |
entry.py : entrées des tableaux de liaisons CharGen / CharGen_C |
32 / 32 | 32 / 32 |
entrées de liaison sur SpinBox_0 / sur un SpinBox |
0 / 0 | 0 / 0 |
uasset58.py events : ComponentBoundEvent |
115 (OnClicked 73, OnValueChanged 15, OnCheckStateChanged 7, OnHovered 7, OnUnhovered 7, OnReleased 2, OnPressed 2, OnTextChanged 2) |
115 (mêmes valeurs) |
SpinBox_0 n'a donc aucune liaison de propriété (ses 2 occurrences hors WidgetTree : variable du widget et table de classe, comme TextBlock_69 hors ses exports) ; tableaux inchangés en nombre ; ComponentBoundEvent_85 reste un OnValueChanged (export #2175 -> #2177). |
C4b — Chaîne de la garde (niveau nœud) : PASS-STATIC (estimé)
python cg2.py (références croisées nœud à nœud, restreintes aux nœuds de CharGen/EventGraph pour éviter l'homonyme GetVisibility_0) :
| Paire | Avant | Après |
|---|---|---|
ComponentBoundEvent_85 <-> IfThenElse_37 |
absent | vrai |
IfThenElse_37 <-> CallFunction_59 (IsValid) ; CallFunction_59 <-> VariableGet_280 (GI) |
absents | vrai ; vrai |
IfThenElse_37 <-> IfThenElse_38 |
absent | vrai |
IfThenElse_38 <-> CallFunction_93 (IsValid) ; CallFunction_93 <-> VariableGet_281 (CharacterBeingEdited) |
absents | vrai ; vrai |
IfThenElse_38 <-> CallFunction_303 (SetMorphTarget) |
absent | vrai |
CallFunction_303 <-> VariableGet_282 (cible) |
vrai | vrai (inchangé) |
ComponentBoundEvent_85 <-> CallFunction_303 (ancien lien direct) |
vrai | faux (coupé comme prévu) |
ComponentBoundEvent_85 <-> CommutativeAssociativeBinaryOperator_28 ; CallFunction_303 <-> …_28 (× -1) |
vrai ; vrai | vrai ; vrai (inchangés) |
Conforme à SPEC § 2.1 et IMPL § 6 au niveau nœud : la garde est en série entre l'événement et SetMorphTarget, le facteur × -1 et la cible sont inchangés. Non établi sans éditeur : quelle sortie (vrai / faux) de chaque IfThenElse est reliée (les sorties « faux » non branchées ne sont pas visibles à ce niveau), la valeur littérale 'Genesis8Female__Chin Type 1 Assist', le bon fonctionnement de la garde en jeu. |
C5b — V1, CompileAllBlueprints : PASS
Journal Saved\Logs\portage\STF_CompileAllBlueprints_20261010-153044.log (482 112 o ; ouvert 15:30:45 locale, soit après la sauvegarde de CharGen à 15:30 : il compile bien e4c2eea6…) :
| Commande | Compte brut |
|---|---|
grep -c 'Loading and Compiling' |
418 |
grep -c 'a réussi' |
418 |
grep -c 'échoué' ; grep -c 'Error:' |
0 ; 0 |
grep -n 'Compiling Completed' |
l. 3234 : Compiling Completed with 0 errors and 0 warnings and 0 blueprints that failed to load. |
grep -n 'Success -' |
l. 3241 : LogInit: Display: Success - 0 error(s), 0 warning(s) |
| ligne CharGen | l. 2798 Loading and Compiling: '/Game/UI/CharGen.CharGen' ; l. 2800 [0109,92] La compilation de CharGen a réussi ! [en 573 ms] (la ligne 2625 est CharGenSlider, autre Blueprint) |
IMPL § 6 (418 / 418, Success, CharGen l. 2800) confirmé. Exécution du commandlet : non rejouée (interdit ici). |
C6b — Journal du PIE STF-T5b_Editor_20261010-143622.log depuis « Created PIE world » : PASS
Fichier 405 483 o, 3078 lignes, ouvert 14:36:23 locale. Created PIE world : 1 occurrence, l. 2419 (12:40:46.338 UTC). Sauvegardes de CharGen : l. 2378 (12:38:04 UTC, avant le PIE : version avec la garde) et l. 3049 (13:30:28 UTC, après l'arrêt du PIE
Shutting down PIE online subsystems l. 3033 à 13:30:04.756 : version avec le pas de 0.5, qui n'a donc pas été jouée en PIE, comme déclaré).
Commande (tail -n +2419) |
Compte brut |
|---|---|
grep -c 'property GI in /Game/UI/CharGen|propriété GI dans /Game/UI/CharGen' |
0 (0 aussi dans tout le journal ; 1 dans le journal J1 de STF-T4 selon la fiche) |
grep -c 'GI dans /Game/UI' |
0 |
grep -c 'PIE: Error' |
5 |
grep -c 'LogScript: Warning' |
9 |
Fatal error / Assertion failed / Unhandled |
0 |
Les 5 PIE: Error restants : |
|
1.-4. (12:40:46.605 UTC, l. 135, 139, 143, 147 depuis la ligne 2419) : StreetMapStatic dans /Game/BPs/GI.GI_C (×2) et Accès à None, lecture de la structure Amenity (×2), fonction Works Here, Blueprint Person… : carte de rues absente, hors périmètre, identiques à ceux de la partie (a). |
|
5. (12:41:01.319 UTC, l. 396) : Accès à "Aucun" … CallFunc_GetSkeletalMesh_ReturnValue en dehors d'une Uclass, nœud Branche / ForEachLoop, fonction Populate Genital Sliders Daz, Blueprint CharGen : nœud d'origine, hors périmètre (candidat noté au registre par l'orchestrateur, IMPL § 6) ; il ne concerne ni SpinBox_0 ni GI. |
|
| Le gain se confirme : 0 « property GI in /Game/UI/CharGen » (1 avant). Limite : un journal propre ne prouve pas l'effet en direct du morph (déclaré par l'utilisateur) ni le comportement du pas de 0.5. |
b4. Diff lu en entier
git diff --stat STF-T5b-avant -- Content Config Source : 1 fichier binaire (CharGen.uasset, 5 046 138 -> 5 054 038 o). Aucun diff texte ; le « diff » est le diff d'exports de C1b (1 retiré / 5 ajoutés / 6 tailles communes modifiées, tous annoncés ou expliqués, sauf le renommage de ComponentDelegateBinding et le résidu d'en-tête) complété par C2b-C4b.
b5. Verdict
Verdict global de la partie (b) : PASS-STATIC (aucun FAIL ; réserves ci-dessous). Par contrôle :
- C0b état des fichiers (CharGen, MainMenu, GI, StatMenu,
.sav) : PASS - C1b diff d'exports : PASS (4 ajouts attendus ;
ComponentDelegateBinding_1 -> _3etSpinBox_0+83 o signalés ;ComponentBoundEvent_85/CallFunction_303sans changement de taille) - C2b propriétés de
SpinBox_0: PASS-STATIC (Delta 0.5, snap vrai, décimales 1 / 1, bornes inchangées) - C3b liaisons et événements : PASS (aucune liaison, 32 / 32 entrées, 115 / 115 événements,
OnValueChanged15) - C4b garde (niveau nœud) : PASS-STATIC
- C5b V1 : PASS (418 / 418, 0 / 0, ligne CharGen)
- C6b journal PIE : PASS (0 « property GI in /Game/UI/CharGen » ; 5 erreurs hors périmètre listées)
Réserves : (1) garde et sorties « faux » lues au niveau nœud seulement ; (2) le pas de 0.5 n'a pas été joué en PIE (déclaré par l'IMPL, confirmé par le journal) ; (3) effet en direct du morph : déclaré par l'utilisateur ; (4) résidu d'en-tête (+3 327) non décodé.
Preuve en jeu J de la partie (b) (archive Development :
property GI in /Game/UI/CharGen.CharGen_C= 0, effet en direct, pas de 0.5) : à faire par l'utilisateur.
b6. Points à confirmer par l'utilisateur
- 1. Accepter l'ajout du pas de 0.5 sur « Counteract Vagina Deform » (
Delta0.5, snap, 1 décimale) : décision prise pendant le PIE, hors SPEC § 2 ; la valeur affichée et tapée est arrondie au demi-pas (même nature que E3 de STF). - 2. Preuve en jeu J de (b) : glisser le champ en création de personnage (morph visible en direct, valeurs par pas de 0.5), 0 occurrence au journal.
- 3. Commit :
CharGen.uassetseul pour (b) (ticket STF-R5/R6 selon le gardien) ; ne pas y mêlerMainMenu.uassetde (a) si (a) a son propre commit. - 4.
Populate Genital Sliders Daz(GetSkeletalMeshnul) : décider s'il devient un lot séparé (hors STF).
Tours (b) : 11 / 20
b7. Décisions sur b6 (orchestrateur, 2026-10-10)
- Pas de 0,5 : décision de l'utilisateur pendant le PIE (« 0.5 »), accordée par Big Boss dans le même BUILD-GO ; écart à STF-T5-SPEC § 2 consigné ici et dans STF-T5-IMPL § 6.
- Commit de (b) avant la preuve en jeu : décision de l'utilisateur (« Maintenant, comme (a) ») ; J de T5 (a + b) sur la prochaine archive.
- Commit :
Content/UI/CharGen.uassetseul côté contenu (MainMenu déjà commité en 547dcdd). PopulateGenitalSlidersDaz: candidat noté au registre STF, hors STF.