33 KiB
Fiche OUTIL1-D2 — Niveau N3 — Modèle prévu sonnet | utilisé claude-sonnet-5-5 — Agent lp-impl — Skills sk-protocol, ue-cpp-build, lp-build, lp-ue-compat
OUTIL1-D2-IMPL : fusion de F5, DESTROY et hitch de logstats.py dans Tools\LogStats.ps1 (2026-10-14)
Hors gabarit : les sections 7 à 11 s'ajoutent aux six sections IMPL, à la demande de la fiche (MD5 et diff, non-régression collée, PubCheck, ligne d'index). La section 11 rend compte de la reprise OUTIL1-D2-b (trame calculée seulement avec -Stats) et prévaut sur les sections 1, 5, 7 et 8 là où elles diffèrent : celles-ci décrivent l'état intermédiaire (MD5 FB7C7143E70F1CC0242C98EDD8B0FE86, trame toujours calculée).
1. Fichiers modifiés (chemins, lignes)
Un seul fichier de code : D:\Game\LifePlay-master 5.8\Tools\LogStats.ps1, 141 → 289 lignes (état final, reprise D2-b), 148 ajoutées, 0 retirée par rapport à l'original (git diff --no-index --numstat : 148 0 ; état intermédiaire : 286 lignes, 145 0). Aucune ligne de l'original n'est touchée. Depuis la reprise D2-b, les numéros de ligne du tableau ci-dessous sont décalés de +1 à partir de la ligne 15 (l'aide gagne une ligne).
| Lignes | Ajout | Source dans logstats.py (ST\STE\STE-J6-TRACE\logstats.py) |
|---|---|---|
| 10-14 | aide .DESCRIPTION : décrit la nouvelle section et rappelle que le corps s'exécute à la lecture |
sans objet (texte) |
| 79 | $rxTramePrefixe : horodatage et numéro de trame |
RX_PREFIX, l. 5 |
| 80 | $rxTrameF5 : « No SkeletalMesh for Component(CharacterMesh0) » par socket et acteur |
RX_F5, l. 6 |
| 81 | $rxTrameDestroy : DESTROY.*index Person_C_n |
RX_DESTROY, l. 7 |
| 82 | $rxTrameHitch : LPDebug hitch : <ms> ms |
RX_HITCH, l. 11 |
| 84-88 | Get-LPCompte (compteur d'un dictionnaire, 0 si absent, comme Counter) |
collections.Counter, l. 58 |
| 90-96 | Get-LPGroupesF5 : min(Head // 2, pelvis, chestUpper, hip) |
l. 61 et l. 67 |
| 98-100 | Format-LPDecimale1 : x.ToString('F1') invariant |
:.1f, l. 84 |
| 102 | Format-LPListe : [a, b, c] comme le repr d'une liste Python |
l. 76-77 |
| 104-169 | Get-LPTrameStats : une boucle sur les lignes, mêmes étapes que la boucle Python |
trame l. 27-31, F5 l. 34-36 (condition 'LogSkinnedMeshComp' in s incluse, l. 35), DESTROY l. 37-39, hitch l. 50-52, groupes l. 58-62, DESTROY par trame l. 64-78, hitch l. 82-84 |
| 171-187 | Show-LPTrame : mêmes libellés que print |
l. 54, 55 (partie F5), 56, 62, 63, 76-78, 84 |
| 189-208 | Get-LPTrameMesures : une ligne par mesure pour -Compare (plus une ligne F5 socket <nom> par socket) |
sans équivalent (le Python ne compare pas) |
| 244 (état final) | propriété cachée _Trame de Get-LogStats, calculée seulement si -Stats : $(if ($Stats) { Get-LPTrameStats $lines }) (préfixe _ : exclue du Format-List et de la table -Compare, donc sorties anciennes intactes) |
|
| 252 (état final) | Show appelle Show-LPTrame après la table « Acces a None par propriete », si _Trame existe |
|
| 265-271 (état final) | fin de -Compare, dans un if ($Stats) { … } : ligne vide, titre, table Mesure / A / B |
Comportement ajouté dans la sortie (état final) : section « Trame, F5, DESTROY, hitch (motifs de logstats.py) » seulement avec -Stats, après les compteurs normalisés (mode un journal) et en fin de -Compare -Stats. Sans -Stats, ainsi qu'en -Pattern et -Tail, la sortie et la durée sont celles de l'original (mesuré § 11).
Corps du script : inchangé, il s'exécute toujours à la lecture (pas de mode bibliothèque ; les fonctions et regex ajoutées sont définies avant le corps, comme $reNone). EssaiJournal.ps1 n'est pas touché et ne peut toujours pas appeler LogStats.ps1 sans l'exécuter.
2. Écarts à la SPEC (obligatoire)
La fiche tient lieu de SPEC. Écarts et choix, un par ligne :
- E1 — « nouvelles colonnes ou sections » : choix de sections après la sortie existante (propriété cachée
_Trame), plutôt que des colonnes dans leFormat-Listou la table-Compare: c'est ce qui garde l'ancienne sortie comme préfixe exact de la nouvelle (MD5 égaux, § 8). - E2 — Familles de
logstats.pynon refusionnées car déjà dansLogStats.ps1:lignes(Lignes),"No SkeletalMesh" (toutes lignes)(NoSkeletalMesh),index SPAWN(Personnages). Mesuré égal sur 3 journaux : lignes 13402 / 17963 / 85697, NoSkeletalMesh 2717 / 1473 / 44635, SPAWN 19 / 11 / 345 des deux côtés (§ 8). Les définitions diffèrent pourtant (RX_SPAWNl. 8 est(Person_C_\d+)\s+index SPAWN, le PS compteindex SPAWNsans casse ;-matchdu PS est insensible à la casse,indu Python ne l'est pas) : égalité constatée, non démontrée. - E3 — Famille de
logstats.pynon fusionnée, hors fiche : « Accessed None par couple (fonction, propriété) » (logstats.pyl. 9-10, 42-49, 79-81 ;LogStats.ps1ne compte que par propriété). À décider (§ 5, P4). - E4 — Un
LPDebug hitch : <nombre illisible> ms(motif[\d.]+accepte « 1.2.3 ») fait planterlogstats.py(float()), alors que le PS ignore la valeur et l'écrit sur une ligne « valeurs illisibles ignorées » (affichée seulement si > 0). Aucun cas dans les journaux essayés. - E5 —
sum(hs)du Python ≥ 3.12 est compensé (Neumaier) ; le PS additionne simplement dans l'ordre croissant. Total identique à 0,1 s près sur 3 journaux ; un écart d'arrondi n'est possible qu'à une frontière de.1f. - E6 — Pas de code de sortie 0/1/2 :
logstats.pylu en entier n'en a aucun (pas desys.exit) ; la note du CSV (« codes 0/1/2 ») ne vise pas ce script. Interface-Log/position 0 deLogStats.ps1gardée. - E7 — « Avant » = l'instantané exécuté sur place (
D:\Game\LifePlay_Backup_5.5\OUTIL1-D2\Tools\LogStats.ps1), sans copie ailleurs ; pour-Latest,-LogDirest passé explicitement aux deux versions (le script d'instantané n'est pas dansTools\). - E8 —
Tools\LISEZMOI.mdn'existe pas dans la copie 5.8 (Get-ChildItem -Recurse -Filter LISEZ*sousTools: vide ;Glob **/LISEZMOI.md: aucun). Non créé (la fiche l'interdit) ; ligne proposée § 10. - E9 — Entorse à la règle de shell : ma première commande de non-régression se terminait par
2>&1 | head -0; echo(ajouté par erreur). Elle a tourné (aucun refus du crochet), sortie jetée parhead -0, lecture seule sur les journaux ; rejouée aussitôt sans cet ajout, c'est celle-là qui est collée en § 8. Aucune autre commande ne contenait>,<,->,tee, heredoc oupython -c. Autre entorse, à la règle « Grep/Glob/Read plutôt que le shell » desk-protocol§ 7 : un listagels -la <dossier> | head -80du dossier de sauvegarde (lecture seule). - E10 — Coût en temps : réglé par la décision de l'orchestrateur en reprise D2-b (trame seulement avec
-Stats, § 11).
3. Instantané (lot, manifeste)
pwsh -NoProfile -Command "& 'D:\Game\LifePlay-master 5.8\Tools\Snapshot.ps1' -Lot OUTIL1-D2 -Path @('Tools\LogStats.ps1')" avant toute écriture. Sortie :
sauvegarde : Tools\LogStats.ps1 (5CD51FB7F1CFA10CFCC7DD4B2FE3F65D)
1 entree(s) ajoutee(s) a D:\Game\LifePlay_Backup_5.5\OUTIL1-D2\manifest.csv
Copie : D:\Game\LifePlay_Backup_5.5\OUTIL1-D2\Tools\LogStats.ps1. Retour arrière proposé (non lancé) : Tools\Restore.ps1 -Lot OUTIL1-D2.
4. Ce qui n'a pas été fait
- Famille « Accessed None par (fonction, propriété) » de
logstats.py(E3) : reste, décision de l'orchestrateur (non fusionnée pour l'instant) ; mode bibliothèque (interdit par la fiche sans signalement). L'interrupteur de la trame est-Stats(reprise D2-b, § 11). logstats.pynon touché, non déplacé : mis à la corbeille seulement sur décision après VAL (la fiche ne le demande pas).Tools\LISEZMOI.md: absent, non créé (E8). Aucun autre outil touché.- Pas de compilation (sans objet : script PowerShell). Analyse syntaxique seule :
Parser::ParseFile→erreurs de syntaxe : 0. - Non mesuré : journaux de la 5.8 (jeu empaqueté) et sortie sous Windows PowerShell 5.1 (le fichier est UTF-8 sans BOM comme avant : 5.1 lisait déjà mal les 2 lignes accentuées de
$reNoneetIndexHorsBornes).
5. Points à confirmer en VAL (un par ligne)
- P1 — Rejouer la non-régression de l'ancienne sortie sur au moins deux journaux (§ 8) : ancienne sortie = préfixe exact de la nouvelle, MD5 égaux.
- P2 — Rejouer la parité F5 / DESTROY / hitch avec
python -I -X utf8 logstats.py T <journal>sur COMMUN-10 et COMMUN-13 (9 lignes égales sur chacun). - P3 — Réglé en reprise D2-b (décision de l'orchestrateur) : la trame n'est calculée qu'avec
-Stats(un journal et-Compare). Constat de la première version (trame toujours calculée) : 0,57 s → 8,31 s sur COMMUN-2 (85 697 lignes, 15,8 Mo), +0,8 s sur 13 000-18 000 lignes ; micro-mesure : boucle du préfixe 1,6 s, troisContains0,9 s, boucle F5 avec dictionnaires 3,4 s. État final : durées au § 11. - P4 — « Accessed None par (fonction, propriété) » de
logstats.py: décision de l'orchestrateur, non fusionnée pour l'instant, à noter en reste (E3). - P5 — Vérifier que
Tools\EssaiJournal.ps1et les autres appelants deLogStats.ps1n'ont pas besoin d'être revus : aucun appelant n'a été lu ni modifié ici. - P6 — Fins de ligne et encodage (état final) : LF seul (CR = 0, LF = 289), pas de BOM (octets de tête
60,35,10), 12 octets non ASCII comme avant (§ 11). - P7 — PubCheck sur le script et sur ce livrable = 0 (§ 9 et § 11).
- P8 —
Tools\LISEZMOI.mdabsent : la fiche OUTIL1-A le crée ; la ligne du § 10 lui est transmise par l'orchestrateur.
6. Fichiers à commiter
Dépôt jeu (D:\Game\LifePlay-master 5.8, branche ue5.8) :
- A
Docs/Migration/etat/EVAL2/AMEL1/OUTIL1/OUTIL1-D2-IMPL.md
Tools/LogStats.ps1 est modifié sur le disque seulement : Tools/ est ignoré (git check-ignore -v Tools/LogStats.ps1 → .gitignore:91:/Tools/), rien à commiter pour le script. Fichier de travail hors dépôt : fmt_check.py (dossier temporaire de la session, essai unique du format .1f), à ne pas commiter.
7. MD5 avant / après et diff résumé
| Octets | Lignes | MD5 | |
|---|---|---|---|
| Avant (instantané) | 7578 | 141 | 5CD51FB7F1CFA10CFCC7DD4B2FE3F65D |
Après, état intermédiaire (avant la reprise D2-b ; copie D:\Game\LifePlay_Backup_5.5\OUTIL1-D2b\Tools\LogStats.ps1) |
15841 | 286 | FB7C7143E70F1CC0242C98EDD8B0FE86 |
| Après, état final (reprise D2-b) | 16027 | 289 | A821D3C74FFA7A63A15A5754706A6B1E |
Commande : [IO.File]::ReadAllBytes et Get-FileHash -Algorithm MD5 (pwsh 7.6.6). Sortie brute de l'état intermédiaire (celle de l'état final est au § 11) :
taille=15841 premiers octets=60,35,10,46
CR=0 LF=286
non-ASCII=12
MD5 apres=FB7C7143E70F1CC0242C98EDD8B0FE86
MD5 avant (instantane)=5CD51FB7F1CFA10CFCC7DD4B2FE3F65D
lignes=286
145 0 D:/Game/{LifePlay_Backup_5.5/OUTIL1-D2 => LifePlay-master 5.8}/Tools/LogStats.ps1
Diff résumé : 5 emplacements d'ajout pur (aide l. 10-14 ; bloc de fonctions l. 78-208 ; _Trame l. 243 ; appel l. 251 ; fin de -Compare l. 264-268). Modifications faites avec l'outil Edit (aucun script de remplacement).
8. Non-régression (collée)
Les blocs 8.2 à 8.5 sont ceux de l'état intermédiaire (trame toujours calculée, MD5 FB7C7143…) ; ils restent exacts pour la parité Python (8.4) et le format (8.1). La non-régression de l'état final, avec et sans -Stats, est au § 11.
Trace : D:\Game\LifePlay-master 5.8\Docs\Migration\etat\EVAL2\AMEL1\OUTIL1\OUTIL1-D2-IMPL.md § 8 — Fiche OUTIL1-D2 — 2026-10-14
Commande : pwsh -NoProfile -Command '<bloc>' où <bloc> exécute, pour chaque journal $j, l'ancien script
($old = D:\Game\LifePlay_Backup_5.5\OUTIL1-D2\Tools\LogStats.ps1) et le nouveau
($new = D:\Game\LifePlay-master 5.8\Tools\LogStats.ps1) : @(& $old $j | Out-String -Stream) et
@(& $new $j | Out-String -Stream), compare $o à $n[0..($o.Count-1)] (Compare-Object -CaseSensitive) et
leurs MD5 (UTF-8, lignes jointes par LF), puis python -I -X utf8
"D:\Game\LifePlay-master 5.8\Docs\Migration\etat\ST\STE\STE-J6-TRACE\logstats.py" T $j
(Python 3.14.4) ; les lignes de la section ajoutée sont comparées une à une à celles du Python
(la ligne « lignes= » devient « horodatage premier= … ; blocs_trame= », la ligne
« "No SkeletalMesh" … ; F5 … » devient « F5 LogSkinnedMeshComp=n », « DESTROY=n ; index SPAWN=s »
devient « DESTROY=n » ; lignes « AN » et « Accessed None » écartées). Texte complet du bloc :
transcription de la session.
Environnement: copie 5.8, branche ue5.8, Tools\LogStats.ps1 MD5 FB7C7143… ; pwsh 7.6.6 ; Python 3.14.4 ;
journaux D:\Game\LifePlay_Backup_5.5\COMMUN-10\essais\commun10_LifePlay.log (13402 lignes),
COMMUN-13\essais\commun13_LifePlay.log (17963), COMMUN-2\essais\LifePlay_commun2_definitif_BOSS.log
(85697), en lecture seule ; machine non chargée par ailleurs (un seul processus lancé à la fois).
Attendu : ancienne sortie = préfixe exact de la nouvelle (MD5 égaux) ; 9 lignes de famille nouvelle égales
à celles du Python ; modes -Pattern/-Tail/-Latest sans -Stats identiques de bout en bout.
Obtenu : ci-dessous.
Lecture : le test « modes inchangés » du premier essai était faux (splat d'un tableau = arguments positionnels,
0 ligne comparée des deux côtés) ; refait avec des tables de hachage. Une fonction nommée H masquée par
l'alias Get-History a vidé les MD5 du premier essai ; refait avec Md5Texte. Les blocs ci-dessous
viennent des essais refaits. Le bloc COMMUN-10 / COMMUN-13 / COMMUN-2 « sections ajoutées » vient de l'état
final du fichier pour COMMUN-2 et -Compare, de l'état précédent (avant retrait d'un contournement
d'arrondi inutile dans Format-LPDecimale1) pour les sections COMMUN-10 et COMMUN-13 ; les deux
journaux ont été rejoués dans l'état final (lignes « familles nouvelles = logstats.py : True »).
8.1 Format .1f : Python contre .NET (pwsh 7.6.6), 11 valeurs dont les égalités exactes
--- python format .1f --- .NET F1 direct
0.25 0.2 0.25 0.2
0.75 0.8 0.75 0.8
0.35 0.3 0.35 0.3
12.05 12.1 12.05 12.1
0.45 0.5 0.45 0.5
0.15 0.1 0.15 0.1
33.25 33.2 33.25 33.2
1234.75 1234.8 1234.75 1234.8
2.65 2.6 2.65 2.6
100.05 100.0 100.05 100.0
7.95 8.0 7.95 8.0
(Les deux listes sont les deux sorties du même essai, mises côte à côte ici ; elles sont identiques ligne à ligne. Un premier contournement d'arrondi supposait que .NET arrondit 0.25 vers le haut : faux sur cette version, retiré.)
8.2 Modes existants : sortie entière identique (état final)
=== modes inchanges : sortie entiere identique
Log,Max,Pattern : identique=True lignes=15 MD5 606CE80D352AF052231AC22A9E1D1ED9 / 606CE80D352AF052231AC22A9E1D1ED9
Log,Tail : identique=True lignes=8 MD5 43CCD36CEBA64CF2CA1F75D7D645F12E / 43CCD36CEBA64CF2CA1F75D7D645F12E
Context,Log,Max,Pattern : identique=True lignes=10 MD5 C4F78E532D3D46426AE909AA22887D8B / C4F78E532D3D46426AE909AA22887D8B
Latest,LogDir,Max,Pattern : identique=True lignes=6 MD5 7C4568BD46033477107E983F6B32E7FC / 7C4568BD46033477107E983F6B32E7FC
Latest,LogDir,Tail : identique=True lignes=7 MD5 2212B5C5D580C0DEFE0ABFE868B07470 / 2212B5C5D580C0DEFE0ABFE868B07470
8.3 Mode un journal : ancienne sortie = préfixe de la nouvelle (état final)
COMMUN-10 : ancien = prefixe du nouveau (ligne a ligne, casse) : True ; lignes ancien=34 nouveau=44 ; MD5 ancien=51F74ED5371C255296C739DF1E7E1E41 MD5 prefixe nouveau=51F74ED5371C255296C739DF1E7E1E41
COMMUN-13 : ancien = prefixe du nouveau (ligne a ligne, casse) : True ; lignes ancien=31 nouveau=41 ; MD5 ancien=A41B5DF2EF7044E92B292E56BA1C429C MD5 prefixe nouveau=A41B5DF2EF7044E92B292E56BA1C429C
COMMUN-2 : ancien = prefixe du nouveau (ligne a ligne, casse) : True ; lignes ancien=39 nouveau=49 ; MD5 ancien=71CCF3712B11995BE83837B33B2E7771 MD5 prefixe nouveau=71CCF3712B11995BE83837B33B2E7771
Journal de compilation U7_Build_20261009-103224.log (aucune famille) : True ; lignes ancien=23 nouveau=32 ; MD5 186E185137E1A09C4EECDFA283BA8271 (égaux)
(Les étiquettes COMMUN-… sont ajoutées ici ; la sortie du script porte « ancien = prefixe du nouveau … » sans étiquette.)
Familles anciennes lues dans la sortie ancienne, comparées au Python :
COMMUN-10 : Lignes 13402 / Personnages 19 / NoSkeletalMesh 2717 | python : lignes=13402, "No SkeletalMesh"=2717, index SPAWN=19
COMMUN-13 : Lignes 17963 / Personnages 11 / NoSkeletalMesh 1473 | python : lignes=17963, "No SkeletalMesh"=1473, index SPAWN=11
COMMUN-2 : Lignes 85697 / Personnages 345 / NoSkeletalMesh 44635 | python : lignes=85697, "No SkeletalMesh"=44635, index SPAWN=345
8.4 Nouvelles familles contre logstats.py
COMMUN-10, sortie PS (section ajoutée) puis sortie brute du Python :
Trame, F5, DESTROY, hitch (motifs de logstats.py) :
horodatage premier=2026.10.14-10.29.27:693 dernier=2026.10.14-10.42.43:877 ; blocs_trame=2266
F5 LogSkinnedMeshComp=2712
F5 par socket : {'CenterBrow': 223, 'Head': 932, 'pelvis': 519, 'chestUpper': 519, 'hip': 519}
groupes de 5 (Head x2, pelvis, chestUpper, hip) par bloc et acteur = 422
DESTROY=15
DESTROY avec groupe F5 dans la trame du DESTROY (meme bloc, meme acteur) = 0 / 15 ; ex. lignes []
DESTROY sans groupe dans leur trame = 15 ; ex. lignes [2186, 2207, 2732]
DESTROY avec groupe dans la trame suivante (bloc+1) = 13 / 15
LPDebug hitch : n=90 max=3888.4 ms mediane=79.7 ms total=16.4 s
--- python
== T : D:\Game\LifePlay_Backup_5.5\COMMUN-10\essais\commun10_LifePlay.log
lignes=13402 premier=2026.10.14-10.29.27:693 dernier=2026.10.14-10.42.43:877 blocs_trame=2266
"No SkeletalMesh" (toutes lignes)=2717 ; F5 LogSkinnedMeshComp=2712
F5 par socket : {'CenterBrow': 223, 'Head': 932, 'pelvis': 519, 'chestUpper': 519, 'hip': 519}
groupes de 5 (Head x2, pelvis, chestUpper, hip) par bloc et acteur = 422
DESTROY=15 ; index SPAWN=19
DESTROY avec groupe F5 dans la trame du DESTROY (meme bloc, meme acteur) = 0 / 15 ; ex. lignes []
DESTROY sans groupe dans leur trame = 15 ; ex. lignes [2186, 2207, 2732]
DESTROY avec groupe dans la trame suivante (bloc+1) = 13 / 15
Accessed None : 10 lignes, 9 couples (fonction, propriete)
LPDebug hitch : n=90 max=3888.4 ms mediane=79.7 ms total=16.4 s
COMMUN-13 (PS puis Python, mêmes libellés) :
horodatage premier=2026.10.14-13.54.52:970 dernier=2026.10.14-14.21.46:657 ; blocs_trame=2573
F5 LogSkinnedMeshComp=1473
F5 par socket : {'CenterBrow': 247, 'Head': 377, 'pelvis': 283, 'chestUpper': 283, 'hip': 283}
groupes de 5 (Head x2, pelvis, chestUpper, hip) par bloc et acteur = 104
DESTROY=11
DESTROY avec groupe F5 dans la trame du DESTROY (meme bloc, meme acteur) = 0 / 11 ; ex. lignes []
DESTROY sans groupe dans leur trame = 11 ; ex. lignes [2756, 2776, 3823]
DESTROY avec groupe dans la trame suivante (bloc+1) = 6 / 11
LPDebug hitch : n=56 max=1350.7 ms mediane=107.5 ms total=10.0 s
--- python (mêmes valeurs : lignes=17963 … blocs_trame=2573 ; F5=1473 ; groupes=104 ; DESTROY=11 ; hitch n=56 max=1350.7 ms mediane=107.5 ms total=10.0 s)
COMMUN-2 (état final, sortie PS ; le Python donne les mêmes 9 lignes) :
horodatage premier=2026.10.13-16.31.24:563 dernier=2026.10.13-17.13.21:321 ; blocs_trame=19538
F5 LogSkinnedMeshComp=44630
F5 par socket : {'CenterBrow': 14652, 'Head': 9689, 'pelvis': 6763, 'chestUpper': 6763, 'hip': 6763}
groupes de 5 (Head x2, pelvis, chestUpper, hip) par bloc et acteur = 3026
DESTROY=340
DESTROY avec groupe F5 dans la trame du DESTROY (meme bloc, meme acteur) = 10 / 340 ; ex. lignes [8414, 10957, 12128]
DESTROY sans groupe dans leur trame = 330 ; ex. lignes [4083, 4085, 4087]
DESTROY avec groupe dans la trame suivante (bloc+1) = 80 / 340
LPDebug hitch : n=108 max=3216.1 ms mediane=71.0 ms total=13.8 s
Verdict de la comparaison ligne à ligne, état final :
D:\Game\LifePlay_Backup_5.5\COMMUN-10\essais\commun10_LifePlay.log : familles nouvelles = logstats.py : True (9 lignes python, 9 lignes PS)
D:\Game\LifePlay_Backup_5.5\COMMUN-13\essais\commun13_LifePlay.log : familles nouvelles = logstats.py : True (9 lignes python, 9 lignes PS)
COMMUN-2 : familles nouvelles = logstats.py : True (9 lignes python, 9 lignes PS)
8.5 -Compare (ancien = préfixe du nouveau) et cas sans famille
=== Compare COMMUN-10 / COMMUN-13 (etat final)
ancien = prefixe du nouveau (ligne a ligne, casse) : True ; lignes ancien=25 nouveau=47 ; MD5 ancien=5A38763669792C4C5516C3768F27F8E5 MD5 prefixe nouveau=5A38763669792C4C5516C3768F27F8E5
Trame, F5, DESTROY, hitch (motifs de logstats.py) :
Mesure A B
------ - -
BlocsTrame 2266 2573
F5LogSkinnedMeshComp 2712 1473
F5Groupes5 422 104
Destroy 15 11
DestroyAvecGroupeF5 0 0
DestroySansGroupe 15 11
DestroyGroupeTrameSuivante 13 6
HitchN 90 56
HitchMaxMs 3888.4 1350.7
HitchMedianeMs 79.7 107.5
HitchTotalS 16.4 10.0
F5 socket CenterBrow 223 247
F5 socket Head 932 377
F5 socket pelvis 519 283
F5 socket chestUpper 519 283
F5 socket hip 519 283
Deux journaux de compilation (U7_Build_20261009-103224.log, U7_Build_20261009-101759.log, aucune ligne de trame) : -Compare ancien = préfixe du nouveau (25 → 39 lignes, MD5 1AF8A6538644AF0FDB94082CE59B639B égaux), table à zéros ; mode un journal : section à zéros (horodatage premier=None dernier=None ; blocs_trame=0, F5 par socket : {}, DESTROY=0, pas de ligne hitch).
-Stats -Pattern DESTROY -Max 1 -Context 0 : la section apparaît après « Acces a None par propriete (15 premieres) » et avant « === Motif 'DESTROY' : 34 lignes sur 13402 ». Le motif libre DESTROY (34 lignes) n'est pas la définition de logstats.py (15 DESTROY rattachés à un index Person_C_n) : deux comptes différents, tous deux justes.
Durées (même journal COMMUN-2, 85 697 lignes) : ancien 0,57 s, nouveau 8,31 s (premier essai : 0,53 s et 8,59 s) ; COMMUN-10 : 0,28 s et 1,10 s.
9. PubCheck
- Script :
pwsh -NoProfile -File "D:\Game\LifePlay-master 5.8\Tools\PubCheck.ps1" -Fichiers "D:\Game\LifePlay-master 5.8\Tools\LogStats.ps1"→PubCheck : 0 constat(s), code 0. - Livrable et script ensemble :
pwsh -NoProfile -File "D:\Game\LifePlay-master 5.8\Tools\PubCheck.ps1" -Fichiers "D:\Game\LifePlay-master 5.8\Docs\Migration\etat\EVAL2\AMEL1\OUTIL1\OUTIL1-D2-IMPL.md" "D:\Game\LifePlay-master 5.8\Tools\LogStats.ps1"→PubCheck : 0 constat(s), code 0, rejoué après les retouches de E9 et de ce paragraphe, avant la reformulation de cette seule phrase (livrable en LF seul, CR = 0).
10. Ligne d'index proposée pour Tools\LISEZMOI.md (fichier absent, non créé)
| LogStats.ps1 | Compteurs normalisés d'un journal (Accessed None, SPAWN, erreurs), comparaison A/B (-Compare), recherche (-Latest, -Pattern, -Tail) ; depuis OUTIL1-D2 aussi trame, F5 par socket et groupes de 5, DESTROY par trame, LPDebug hitch (ex logstats.py de STE-J6-TRACE) | pwsh -NoProfile -File Tools\LogStats.ps1 <journal> [-Stats] ; pwsh -NoProfile -Command "& 'Tools\LogStats.ps1' -Compare a,b" |
11. Reprise OUTIL1-D2-b : trame calculée seulement avec -Stats (2026-10-14)
Décisions de l'orchestrateur : (P3) trame, F5 par socket, DESTROY par trame et hitch calculés seulement avec -Stats ; sans -Stats, sortie et temps comme avant, -Compare compris ; (P4) « Accessed None par fonction et propriété » non fusionnée, à noter en reste ; (LISEZMOI) créé par la fiche OUTIL1-A, ligne d'index du § 10 transmise.
Modifications (outil Edit, 4 remplacements dans Tools\LogStats.ps1) : aide l. 10-15 (4 lignes au lieu de 3 : « SEULEMENT avec -Stats »), l. 244 _Trame = $(if ($Stats) { Get-LPTrameStats $lines }), l. 252 if ($s._Trame) { Show-LPTrame $s._Trame }, l. 265-271 bloc de fin de -Compare mis dans if ($Stats) { … }. $Stats est lu dans la portée du script comme $Top. Rien d'autre : le corps s'exécute toujours à la lecture.
Instantané avant cette reprise : pwsh -NoProfile -Command "& 'D:\Game\LifePlay-master 5.8\Tools\Snapshot.ps1' -Lot OUTIL1-D2b -Path @('Tools\LogStats.ps1')" →
sauvegarde : Tools\LogStats.ps1 (FB7C7143E70F1CC0242C98EDD8B0FE86)
1 entree(s) ajoutee(s) a D:\Game\LifePlay_Backup_5.5\OUTIL1-D2b\manifest.csv
MD5 et diff (état final), sortie brute :
taille=16027 premiers octets=60,35,10,46
CR=0 LF=289
non-ASCII=12
MD5 apres=A821D3C74FFA7A63A15A5754706A6B1E
MD5 original (OUTIL1-D2)=5CD51FB7F1CFA10CFCC7DD4B2FE3F65D
MD5 etat intermediaire (OUTIL1-D2b)=FB7C7143E70F1CC0242C98EDD8B0FE86
lignes=289
148 0 D:/Game/{LifePlay_Backup_5.5/OUTIL1-D2 => LifePlay-master 5.8}/Tools/LogStats.ps1
12 9 D:/Game/{LifePlay_Backup_5.5/OUTIL1-D2b => LifePlay-master 5.8}/Tools/LogStats.ps1
Dernière ligne : contre l'original, 148 ajoutées, 0 retirée ; contre l'état intermédiaire, 12 ajoutées et 9 retirées (aide, trois lignes de la trame, ré-indentation du bloc -Compare).
Trace : D:\Game\LifePlay-master 5.8\Docs\Migration\etat\EVAL2\AMEL1\OUTIL1\OUTIL1-D2-IMPL.md § 11 — Fiche OUTIL1-D2-b — 2026-10-14
Commande : même méthode qu'au § 8 : pour chaque journal, @(& $old … | Out-String -Stream) (original, instantané
OUTIL1-D2) contre @(& $new … | Out-String -Stream) sans puis avec -Stats ; « identique » = même nombre
de lignes et Compare-Object -CaseSensitive vide ; « préfixe » = $o contre $n[0..($o.Count-1)] ; MD5 de
chaque texte ; parité = python -I -X utf8 logstats.py T <journal> (Python 3.14.4) contre les lignes
ajoutées ; durées par Stopwatch autour de chaque appel. Journaux : COMMUN-10, COMMUN-13, COMMUN-2
(D:\Game\LifePlay_Backup_5.5\COMMUN-*\essais\*.log, lecture seule), journaux de compilation
U7_Build_20261009-103224.log et -101759.log (Saved\Logs\portage).
Environnement: copie 5.8, branche ue5.8, Tools\LogStats.ps1 MD5 A821D3C7… ; pwsh 7.6.6 ; Python 3.14.4 ; un seul
processus de mesure à la fois, rien d'autre lancé pendant les durées.
Attendu : sans -Stats : sortie entière identique à l'original et durée de l'ordre de 0,5 s sur COMMUN-2 ;
avec -Stats : original = préfixe exact, 9 lignes de parité Python égales.
Obtenu : ci-dessous.
Lecture : les durées de COMMUN-2 varient de 0,47 à 0,83 s pour l'ORIGINAL d'un appel à l'autre (cache disque,
premier appel) : seule l'échelle (0,5 s contre 9,9 s) compte, pas la deuxième décimale.
11.1 Sans -Stats, avec -Stats, -Compare, autres modes (COMMUN-10 et COMMUN-13)
erreurs de syntaxe : 0
=== D:\Game\LifePlay_Backup_5.5\COMMUN-10\essais\commun10_LifePlay.log
sans -Stats : identique (sortie entiere, casse) : True ; lignes ancien=34 nouveau=34 ; MD5 51F74ED5371C255296C739DF1E7E1E41 / 51F74ED5371C255296C739DF1E7E1E41
avec -Stats : ancien = prefixe du nouveau (ligne a ligne, casse) : True ; lignes ancien=34 nouveau=44 ; MD5 ancien=51F74ED5371C255296C739DF1E7E1E41 MD5 prefixe nouveau=51F74ED5371C255296C739DF1E7E1E41
avec -Stats : parite logstats.py : True (9 lignes python, 9 lignes PS)
=== D:\Game\LifePlay_Backup_5.5\COMMUN-13\essais\commun13_LifePlay.log
sans -Stats : identique (sortie entiere, casse) : True ; lignes ancien=31 nouveau=31 ; MD5 A41B5DF2EF7044E92B292E56BA1C429C / A41B5DF2EF7044E92B292E56BA1C429C
avec -Stats : ancien = prefixe du nouveau (ligne a ligne, casse) : True ; lignes ancien=31 nouveau=41 ; MD5 ancien=A41B5DF2EF7044E92B292E56BA1C429C MD5 prefixe nouveau=A41B5DF2EF7044E92B292E56BA1C429C
avec -Stats : parite logstats.py : True (9 lignes python, 9 lignes PS)
=== -Compare COMMUN-10 / COMMUN-13
sans -Stats : identique (sortie entiere, casse) : True ; lignes ancien=25 nouveau=25 ; MD5 5A38763669792C4C5516C3768F27F8E5 / 5A38763669792C4C5516C3768F27F8E5
avec -Stats : ancien = prefixe du nouveau (ligne a ligne, casse) : True ; lignes ancien=25 nouveau=47 ; MD5 ancien=5A38763669792C4C5516C3768F27F8E5 MD5 prefixe nouveau=5A38763669792C4C5516C3768F27F8E5
lignes ajoutees avec -Stats : 22
=== modes -Pattern / -Tail / -Latest : sortie entiere identique
Log,Max,Pattern : identique (sortie entiere, casse) : True ; lignes ancien=15 nouveau=15 ; MD5 606CE80D352AF052231AC22A9E1D1ED9 / 606CE80D352AF052231AC22A9E1D1ED9
Log,Tail : identique (sortie entiere, casse) : True ; lignes ancien=8 nouveau=8 ; MD5 43CCD36CEBA64CF2CA1F75D7D645F12E / 43CCD36CEBA64CF2CA1F75D7D645F12E
Context,Log,Max,Pattern : identique (sortie entiere, casse) : True ; lignes ancien=10 nouveau=10 ; MD5 C4F78E532D3D46426AE909AA22887D8B / C4F78E532D3D46426AE909AA22887D8B
Latest,LogDir,Max,Pattern : identique (sortie entiere, casse) : True ; lignes ancien=6 nouveau=6 ; MD5 7C4568BD46033477107E983F6B32E7FC / 7C4568BD46033477107E983F6B32E7FC
Latest,LogDir,Tail : identique (sortie entiere, casse) : True ; lignes ancien=7 nouveau=7 ; MD5 2212B5C5D580C0DEFE0ABFE868B07470 / 2212B5C5D580C0DEFE0ABFE868B07470
=== -Stats -Pattern DESTROY (section avant le motif)
Acces a None par propriete (15 premieres) :
Trame, F5, DESTROY, hitch (motifs de logstats.py) :
=== Motif 'DESTROY' : 34 lignes sur 13402 (1 premieres)
=== journaux de compilation (aucune famille)
defaut : identique (sortie entiere, casse) : True ; lignes ancien=23 nouveau=23 ; MD5 186E185137E1A09C4EECDFA283BA8271 / 186E185137E1A09C4EECDFA283BA8271
-Stats : ancien = prefixe du nouveau (ligne a ligne, casse) : True ; lignes ancien=23 nouveau=32 ; MD5 ancien=186E185137E1A09C4EECDFA283BA8271 MD5 prefixe nouveau=186E185137E1A09C4EECDFA283BA8271
Compare sans -Stats : identique (sortie entiere, casse) : True ; lignes ancien=25 nouveau=25 ; MD5 1AF8A6538644AF0FDB94082CE59B639B / 1AF8A6538644AF0FDB94082CE59B639B
Compare -Stats : ancien = prefixe du nouveau (ligne a ligne, casse) : True ; lignes ancien=25 nouveau=39 ; MD5 ancien=1AF8A6538644AF0FDB94082CE59B639B MD5 prefixe nouveau=1AF8A6538644AF0FDB94082CE59B639B
(La section à zéros du journal de compilation avec -Stats est identique à celle du § 8.5 : horodatage premier=None dernier=None ; blocs_trame=0, F5 par socket : {}, DESTROY=0, sans ligne hitch.)
11.2 COMMUN-2 (85 697 lignes, 15,8 Mo) : durées et parité
=== COMMUN-2 (85697 lignes), duree de chaque appel
ancien : 0.83 s
nouveau sans -Stats : 0.59 s
nouveau avec -Stats : 9.89 s
ancien (2e essai) : 0.47 s
nouveau sans -Stats (2e essai) : 0.49 s
sans -Stats : identique=True lignes ancien=39 nouveau=39 MD5 71CCF3712B11995BE83837B33B2E7771 / 71CCF3712B11995BE83837B33B2E7771
avec -Stats : prefixe=True lignes ancien=39 nouveau=49 MD5 71CCF3712B11995BE83837B33B2E7771 / 71CCF3712B11995BE83837B33B2E7771
parite logstats.py (avec -Stats) : True (9 lignes python, 9 lignes PS)
Trame, F5, DESTROY, hitch (motifs de logstats.py) :
horodatage premier=2026.10.13-16.31.24:563 dernier=2026.10.13-17.13.21:321 ; blocs_trame=19538
F5 LogSkinnedMeshComp=44630
F5 par socket : {'CenterBrow': 14652, 'Head': 9689, 'pelvis': 6763, 'chestUpper': 6763, 'hip': 6763}
groupes de 5 (Head x2, pelvis, chestUpper, hip) par bloc et acteur = 3026
DESTROY=340
DESTROY avec groupe F5 dans la trame du DESTROY (meme bloc, meme acteur) = 10 / 340 ; ex. lignes [8414, 10957, 12128]
DESTROY sans groupe dans leur trame = 330 ; ex. lignes [4083, 4085, 4087]
DESTROY avec groupe dans la trame suivante (bloc+1) = 80 / 340
LPDebug hitch : n=108 max=3216.1 ms mediane=71.0 ms total=13.8 s
Synthèse des durées sur COMMUN-2 : original 0,83 s puis 0,47 s ; nouveau sans -Stats 0,59 s puis 0,49 s (de l'ordre de l'original) ; nouveau avec -Stats 9,89 s (première version, trame toujours calculée : 8,31 à 8,59 s ; l'écart de 1,3 s entre ces deux mesures de la même boucle n'est pas expliqué, probablement le bruit de la machine).
11.3 PubCheck (état final)
pwsh -NoProfile -File "D:\Game\LifePlay-master 5.8\Tools\PubCheck.ps1" -Fichiers "D:\Game\LifePlay-master 5.8\Tools\LogStats.ps1" → PubCheck : 0 constat(s), code 0 (script). Livrable et script ensemble, après les retouches de la reprise : PubCheck : 0 constat(s), code 0 (livrable en LF seul, CR = 0 ; MD5 du script relu A821D3C74FFA7A63A15A5754706A6B1E).
Reste : « Accessed None par (fonction, propriété) » (E3) ; Tools\LISEZMOI.md (ligne § 10 à transmettre à OUTIL1-A). Tours de la reprise : 10 / 15 (mon décompte, retouche finale et handback compris).
Tours : 30 / 40 (fiche initiale) ; reprise OUTIL1-D2-b : 10 / 15
Constats
Hest un alias deGet-Historydans pwsh : une fonctionHest masquée (erreur « paramètre Id »).& script @tableaupasse les éléments en arguments positionnels : utiliser une table de hachage pour des paramètres nommés.- Sur pwsh 7.6.6,
Double.ToString('F1')arrondit commeformat(x, '.1f')de Python (égalités exactes comprises) : pas de contournement nécessaire. - Les 3 journaux d'essai ont
Personnages(compté sans casse) égal àRX_SPAWNdu Python ; rien ne garantit l'égalité sur d'autres journaux.