# OUTC-SPEC : scripts de contrôle de la copie partagée (CopieEtat, Verrou, Numero, GitEtat) (2026-10-11) Fiche OUTC-SPEC — Niveau N2 — Modèle prévu opus | utilisé claude-opus-5-5 — Agent orchestrateur (session OUTC) — Skills lp-lot, sk-orchestrate Source : décision E2 de l'utilisateur (« approuvé », 2026-10-11) sur la recommandation R2 de `Docs\Migration\etat\EVAL\EVAL-RAPPORT.md` (§ 6 M1, M4, M5 ; § 8 R2). Mesures de référence : `EVAL-STATS.md` § 7, `EVAL-T6-INV.md` (72 h : 538 empreintes improvisées, 452 relevés de processus, 1 794 appels d'état git, 1 085 appels de verrou et tickets). ## 1. Objectif mesurable Quatre scripts pwsh 7 sous `Tools\` de la copie 5.8 qui remplacent les contrôles mécaniques faits à la main par les sessions principales et par Big Boss, chacun avec une ligne de synthèse, une sortie JSON (`-Json`) et des codes de sortie documentés et prouvés (0 conforme, 1 alerte ou occupé, 2 erreur d'usage ou chemin introuvable) : | Script | Remplace | Appelé par | |---|---|---| | `Tools\CopieEtat.ps1` | `md5sum` / `Get-FileHash`, `Get-Process` / `tasklist`, `git status` de 3 dépôts, recherche des dossiers parasites | session de lot (jointe à `BUILD-DEMANDE` et `BUILD-FIN`), Big Boss (une relance) | | `Tools\Verrou.ps1` | `mkdir` / `New-Item` / lecture de `qui.txt` du verrou (`sk-repo` § 2) | gardien, `Numero.ps1`, orchestrateurs | | `Tools\Numero.ps1` | lectures du journal, du registre, des docs et de `NUMEROS.md` puis ajout manuel d'une ligne (`lp-repo` § 7) | orchestrateur, rédacteur, gardien | | `Tools\GitEtat.ps1` | `git status`, `git diff --name-status`, `git ls-files --eol`, comptes de CR, pour 3 dépôts | tout agent, Big Boss, gardien (étape 2 et 6 de `sk-repo` § 4) | ## 2. Fichiers touchés, ordre Trois fiches IMPL indépendantes (fichiers disjoints), en parallèle : | Fiche | Fichiers (copie 5.8, dépôt jeu) | |---|---| | OUTC-T1 | `A Tools\CopieEtat.ps1`, `M Tools\UeState.ps1` (ajout `-Copie`, `-Json`), `M Tools\FileCheck.ps1` (ajout `-Json`) | | OUTC-T2 | `A Tools\Verrou.ps1`, `A Tools\Numero.ps1` | | OUTC-T3 | `A Tools\GitEtat.ps1` | Règles communes : fichiers neufs en **LF, sans BOM, ASCII seul** (commentaires sans accents, comme `UeState.ps1` et `FileCheck.ps1`, lisibles aussi par Windows PowerShell 5.1) ; `UeState.ps1` et `FileCheck.ps1` restent en LF (`git ls-files --eol` : `i/lf w/lf`). En-tête d'aide `<# .SYNOPSIS .DESCRIPTION .PARAMETER .EXAMPLE #>` au style des scripts existants, codes de sortie listés dans `.DESCRIPTION`. `$ErrorActionPreference = 'Stop'`. Racine du projet par défaut : `Split-Path $PSScriptRoot -Parent`. Aucun script n'écrit dans git (lecture seule : `status`, `diff`, `ls-files`, `show`, `rev-parse`, `cat-file`). Aucune suppression de fichier ou de dossier. Correspondance des dépôts (commune à CopieEtat et GitEtat ; à coder une fois par script, pas de module partagé) : | Copie (`-Copie`, défaut racine du projet) | Addon | Modules | |---|---|---| | nom qui finit par `5.8` (`D:\Game\LifePlay-master 5.8`) | `D:\Game\LifePlay-Sandbox-5.8` | `--git-dir=D:\Game\LifePlay_ModulesGit\5.8.git --work-tree=\Content\Modules` | | nom qui finit par `5.5` | `D:\Game\LifePlay-Sandbox` | `--git-dir=D:\Game\LifePlay_ModulesGit\5.5.git --work-tree=\Content\Modules` | | autre | `-Addon` et `-ModulesGitDir` obligatoires, sinon code 2 | | Piège Modules (`lp-paths` l. 68) : jamais sans `--git-dir`, sinon git remonte au dépôt du jeu. ## 3. Interfaces ### 3.1 `Tools\CopieEtat.ps1` (OUTC-T1) ``` .\Tools\CopieEtat.ps1 -Lot -Assets [-Copie ] [-Reference ] [-Autorise ] [-Addon ] [-ModulesGitDir ] [-Json] [-Sortie ] ``` - `-Assets` : chemins relatifs à la copie (ex. `Content\LifePlay\GI.uasset`) ; 0 à n. - Contrôles, dans cet ordre : 1. **Processus** : appelle `Tools\UeState.ps1 -Copie -Json` (paramètres ajoutés par T1, voir ci-dessous) ; ne retient que les processus dont la **ligne de commande ou le chemin de l'exécutable cite la racine de la copie** (piège P9, `sk-orchestrate` § 3) : un éditeur d'une autre copie ou un build empaqueté hors copie n'est pas une alerte, mais il est listé dans le JSON (`autres`). Alerte `PROC` si au moins un processus de la copie. 2. **Assets** : appelle `Tools\FileCheck.ps1 -ProjectRoot -Json` ; MD5, taille, date. Alerte `ASSET_ABSENT` (introuvable). Avec `-Reference` (JSON d'une sortie précédente de CopieEtat, typiquement celle de `BUILD-DEMANDE`) : alerte `ASSET_MODIFIE` si le MD5 diffère, sauf si l'asset figure dans `-Autorise`. Avec `-Lot` et un manifeste `D:\Game\LifePlay_Backup_5.5\\manifest.csv` présent : colonne `instantane` (identique / modifie / pas sauvegarde) dans le JSON, sans alerte (information). 3. **Git** : `git status --porcelain=v1 --untracked-files=all` sur : jeu `-- Content Config Source` ; addon `-- Config Content Source Plugins/LPSandbox/Source Plugins/LPSandbox/Content` ; Modules (tout le dépôt). Alerte `GIT` pour toute entrée qui n'est ni un asset déclaré, ni dans `-Autorise`, ni (avec `-Reference`) déjà présente avec le même code dans la référence. Dépôt introuvable → code 2. 4. **Parasites** : sous `\Docs\Migration\etat\`, tout dossier nommé `etat` ou `Migration` à n'importe quelle profondeur, et tout dossier `Docs` qui contient un sous-dossier `Migration` (ex. `etat\Docs\Migration\etat\…`, `etat\U58\etat\…`). Un dossier `Docs` sans `Migration` n'est pas un parasite : `etat\publication\PUB2\neutral\Docs\Modding` est une copie de publication légitime (relevé du 2026-10-11, `find -type d`). Alerte `PARASITE` par dossier trouvé (le plus haut seulement, pas ses descendants). Parcours limité aux dossiers (pas de lecture de fichiers). - Sortie console : **une ligne de synthèse** en premier : `COPIE lot= copie= proc= assets= absents= modifies= git=// hors_declares= parasites= alertes=` puis une ligne par alerte (`ALERTE : `). Avec `-Json` : le JSON seul sur la sortie standard (champs `synthese`, `horodatage`, `lot`, `copie`, `processus{copie[],autres[]}`, `assets[]`, `git{jeu[],addon[],modules[]}`, `parasites[]`, `alertes[]`, `code`). `-Sortie` écrit le JSON dans un fichier (UTF-8 sans BOM, LF) en plus de la sortie console normale ; c'est ce fichier qui est joint à `BUILD-DEMANDE` / `BUILD-FIN` et relu par `-Reference`. - Codes : 0 aucune alerte ; 1 au moins une alerte ; 2 usage, copie, dépôt ou `-Reference` introuvable. - Durée visée : < 30 s sur la copie 5.8 (pas de parcours de `Content\`). Ajouts rétrocompatibles (sans paramètre nouveau, sortie **octet pour octet identique** à avant, codes inchangés) : - `UeState.ps1 -Copie ` : ne garde que les processus dont la ligne de commande ou l'exécutable cite la racine (comparaison insensible à la casse, `\` et `/` acceptés, guillemets ignorés) ; `-Json` : tableau JSON (`Kind, Id, Name, Start, Copy, CommandLine`) et ligne de compte dans un champ `compte`, sans texte autour. `-WaitExit` combinable avec `-Copie` (attente des seuls processus de la copie). - `FileCheck.ps1 -Json` : les lignes (`Fins, CRLF, LF, CR, BOM, MD5, Sauv, Contenu, Alerte, Fichier`) plus `Taille` et `Modifie` (date ISO) et les comptes, en JSON seul ; codes inchangés. ### 3.2 `Tools\Verrou.ps1` (OUTC-T2) ``` .\Tools\Verrou.ps1 -Prendre [-Session ] [-Racine ] .\Tools\Verrou.ps1 -Rendre [-Ticket ] [-Racine …] .\Tools\Verrou.ps1 -Etat [-Json] [-Racine …] ``` Racine par défaut : `\Docs\Migration\etat\REPO\VERROU`. Algorithme `sk-repo` § 2 à l'identique : 1. `k` = plus grand numéro parmi les sous-dossiers `` et `-libre` (numérique, pas alphabétique). 2. `-Prendre` : `` sans `-libre` → occupé : affiche `\qui.txt`, `VERROU OCCUPE k= par depuis min`, code 1. Sinon `[IO.Directory]::CreateDirectory` est **interdit** (il réussit si le dossier existe) : création par `New-Item -ItemType Directory` **sans** `-Force` (échoue si le dossier existe) ; échec → occupé, code 1 ; réussite → écrit `\qui.txt` au format en usage (`, session , AAAA-MM-JJ HH:MM`, une ligne, LF), affiche `VERROU PRIS k= ticket=`, code 0. 3. `-Rendre` : le verrou tenu est `` sans `-libre` ; aucun → `VERROU DEJA LIBRE k=`, code 1. Avec `-Ticket` : si `qui.txt` ne commence pas par ce ticket → refus, code 1 (on ne rend pas le verrou d'un autre). Sinon `New-Item -libre` (même règle), `VERROU RENDU k=`, code 0. 4. `-Etat` : `VERROU LIBRE k=` (code 0) ou `VERROU OCCUPE k= par depuis min` (code 1) ; au-delà de 30 min, suffixe `ANCIEN (signaler, ne pas forcer)`. Aucun forçage possible : pas de paramètre `-Forcer`. - Rien n'est jamais supprimé ni renommé. Code 2 : racine introuvable, paramètres incohérents (zéro ou plusieurs actions). ### 3.3 `Tools\Numero.ps1` (OUTC-T2) ``` .\Tools\Numero.ps1 -Reserver CHG|ERR|DOC -Lot -Demandeur [-Nombre ] [-Copie55 ] [-Fichier ] [-Racine ] [-Json] .\Tools\Numero.ps1 -Dernier CHG|ERR|DOC # lecture seule, sans verrou : maxima par source ``` - `-Reserver` : prend le verrou par `Verrou.ps1 -Prendre NUMERO-` (même `-Racine`) ; occupé → code 1, rien écrit. Calcule le maximum entre : copie 5.8 (`Docs\Journal_Modifications.md` motif `^\| CHG-(\d+)` ; `Docs\Registre_Erreurs.md` motif `^\| ERR-(\d+)` ; `Docs\NN_*.md` pour DOC), copie 5.5 (`-Copie55`, défaut `D:\Game\LifePlay-master 5.5` : `Docs\Migration\Journal_Modifications.md`, `Docs\Migration\Registre_Erreurs.md`, `Docs\Migration\NN_*.md` ; absente → avertissement, pas d'échec), et `NUMEROS.md` (colonne Numéro des lignes du type, y compris les plages `058 à 086` : la borne haute compte). Réserve `max+1` (ou `max+1 … max+n` avec `-Nombre`, une ligne par numéro). - Ajoute à la fin du tableau de `NUMEROS.md` la ligne `| CHG | 325 | | ; verrou ; max lus : 5.8= 5.5= registre= | AAAA-MM-JJ | réservé |` (CHG et ERR sur 3 chiffres, DOC sur 2) **en conservant les fins de ligne du fichier** : `NUMEROS.md` est en CRLF (piège DOCSTAT) ; écriture par ajout d'octets UTF-8 sans BOM avec le séparateur détecté dans le fichier, après contrôle que le fichier se termine par un saut de ligne (sinon en ajouter un du même type). Aucune réécriture du reste du fichier : MD5 du préfixe inchangé (vérifié par le script après écriture : les `n` premiers octets d'avant sont identiques, sinon code 2 et message). - Rend le verrou **dans un `finally`** (même en cas d'erreur). - Sortie : `NUMERO RESERVE CHG-325 lot= verrou=` ; `-Json` : `{type, numeros[], lot, verrou, maxima{}}`. - Codes : 0 réservé ; 1 verrou occupé ; 2 erreur (fichier introuvable, contrôle du préfixe en échec). ### 3.4 `Tools\GitEtat.ps1` (OUTC-T3) ``` .\Tools\GitEtat.ps1 [-Depot jeu,addon,modules] [-Chemin ] [-Copie ] [-Addon ] [-ModulesGitDir ] [-Json] [-Tous] ``` - Pour chaque dépôt : branche (`branch --show-current`), HEAD court, puis **une ligne par fichier** modifié, indexé ou non suivi (`status --porcelain=v1 --untracked-files=all`), filtrée par `-Chemin` si donné : ` / CR:/ ` - `name-status` : de `git diff --name-status HEAD -- ` (`M`, `A`, `D`, `R100`…, `?` pour non suivi) ; - `eol` : de `git ls-files --eol -- ` (`i/lf w/crlf` …, `-` si non suivi ou supprimé) ; - `CR` : nombre d'octets `\r` dans le fichier de l'arbre et dans le blob `HEAD:` (`git cat-file -p`), `-` s'il n'existe pas ; binaire (octet nul dans les 8 000 premiers octets) → `bin`. - Alerte `FINS` (ligne suffixée `ALERTE FINS`) : fichier texte suivi dont le CR passe de 0 à > 0 ou de > 0 à 0 entre HEAD et l'arbre, ou fichier mixte (CR > 0 et LF seuls > 0) qui ne l'était pas dans HEAD. - Synthèse : `GIT jeu=@ fichiers ; addon=… ; modules=… ; alertes_fins=`. - `-Tous` : ajoute les fichiers **suivis non modifiés** pour les `-Chemin` donnés (contrôle d'EOL d'un fichier propre, ex. `NUMEROS.md`) ; sans `-Chemin`, `-Tous` est refusé (code 2). - Codes : 0 aucune alerte ; 1 au moins une alerte `FINS` ; 2 dépôt introuvable ou usage. - Le comptage des CR se fait par octets ([IO.File]::ReadAllBytes ; flux de `git cat-file`), **jamais** par `grep -c $'\r$'` (faux sous Git Bash, mémoire `eol-check-etat-lf`). ## 4. Repli, risques, questions - Repli : `Tools\Restore.ps1 -Lot OUTC` (proposé, jamais lancé sans accord) : retire les 4 scripts neufs et rend `UeState.ps1` et `FileCheck.ps1` d'avant (MD5 `A47107D5…`, `CA9CBA16…`, manifeste `D:\Game\LifePlay_Backup_5.5\OUTC`). - R1 : régression de `UeState.ps1` / `FileCheck.ps1`, appelés par `Build.ps1` et cités de 60 à 161 fois → VAL compare la sortie sans paramètre nouveau avant / après (copie sauvegardée lancée en parallèle). - R2 : `Verrou.ps1` testé sur le vrai `VERROU` pendant qu'un gardien travaille → **tous les essais de VAL et d'IMPL sur une racine de test** (`-Racine` dans le scratchpad), le vrai verrou seulement en `-Etat` (lecture). - R3 : `Numero.ps1` qui casse le CRLF de `NUMEROS.md` → essais sur une copie de `NUMEROS.md` (`-Fichier`), contrôle par `FileCheck.ps1` et MD5 du préfixe ; la seule écriture réelle est la réservation du CHG d'OUTC à la DOC. - R4 : parasites — l'arbre `etat\` ne contient aujourd'hui aucun dossier parasite au sens du § 3.1 (relevé du 2026-10-11) : la preuve de l'alerte se fait sur une racine de test (`-Copie` vers une arborescence factice) ou sur un dossier parasite créé puis **laissé** dans le scratchpad, jamais dans la copie. - Q1 (tranchée par l'orchestrateur) : une modification git déjà là au départ n'est pas une alerte si elle est déclarée (`-Assets`, `-Autorise`) ou présente dans la `-Reference` ; sinon `CopieEtat` serait toujours en alerte pendant qu'un lot a des fichiers en cours. ## 5. Interdits Aucune commande git qui écrit ; aucune suppression ; aucun lancement d'éditeur, de commandlet ou de build ; aucun essai d'écriture sur le vrai `VERROU` ni sur le vrai `NUMEROS.md` (sauf la réservation finale du CHG d'OUTC par l'orchestrateur) ; ne modifier ni skills ni agents (lot DOC séparé, décision E5) ; ne pas toucher d'autre fichier de `Tools\` que ceux du § 2. ## 6. Valeurs moteur Sans objet : outillage pwsh, aucune API moteur. ## 7. Critères de VAL (commande → attendu) | # | Commande | Attendu | |---|---|---| | V1 | `git ls-files --eol` / `FileCheck.ps1` sur les 6 fichiers | LF, pas de BOM, ASCII (aucun octet > 0x7F dans les 4 neufs) | | V2 | `UeState.ps1` et `FileCheck.ps1 Tools\UeState.ps1` sans paramètre neuf, version neuve vs copie de `D:\Game\LifePlay_Backup_5.5\OUTC` | sorties identiques (hors horodatage), mêmes codes | | V3 | `CopieEtat.ps1 -Lot OUTC -Assets <2 assets réels> -Sortie ` sur la copie 5.8 (essai réel, BUILD-GO) | 1 ligne de synthèse, JSON valide (`ConvertFrom-Json`), code 0 si rien ne tourne et état déclaré ; MD5 = `FileCheck.ps1` | | V4 | V3 relancé avec `-Reference ` | code 0, `modifies=0` | | V5 | code 1 prouvé : asset absent ; `-Reference` à MD5 falsifié ; fichier non déclaré dans `-Copie` factice ou état réel non déclaré ; parasite dans une copie factice | code 1 et alerte du bon type, à chaque cas | | V6 | code 2 : `-Copie` inexistante ; `-Reference` introuvable | code 2 | | V7 | `Verrou.ps1` sur racine de test : Prendre (0), Prendre encore (1), Etat (1), Rendre -Ticket autre (1), Rendre (0), Rendre encore (1), Etat (0) ; dossier `k+1` créé par un tiers entre la lecture et le `New-Item` (simulé) → 1 ; `-Etat` sur le vrai `VERROU` | codes et dossiers attendus ; vrai verrou lu sans écriture (liste des dossiers identique avant / après) | | V8 | `Numero.ps1 -Reserver CHG -Lot TEST -Fichier -Racine ` deux fois, puis `-Nombre 2` | numéros consécutifs ; préfixe du fichier identique (MD5) ; CRLF conservé (`FileCheck` : CRLF, 0 LF seul) ; verrou rendu (k-libre présent) ; verrou occupé → code 1 sans écriture | | V9 | `GitEtat.ps1` sur les 3 dépôts, puis `-Chemin Docs/Migration/etat/REPO/NUMEROS.md -Tous` | une ligne par fichier, comptes égaux à `git status --porcelain` de chaque dépôt ; NUMEROS : `i/crlf w/crlf`, CR arbre = CR HEAD | | V10 | code 1 de `GitEtat.ps1` : fichier de test dans un dépôt jetable hors des dépôts du projet (`git init` et `git commit` autorisés **dans le scratchpad seulement**, `-Copie`/`-Addon`/`-ModulesGitDir` vers lui) passé de LF à CRLF | `ALERTE FINS`, code 1 | | V11 | relecture des 6 diffs en entier | aucune ligne hors périmètre ; aucune commande git d'écriture, aucun `Remove-Item` | ## 8. Compléments du 2026-10-11 (après envoi des fiches) Demandes de Big Boss (premier utilisateur de CopieEtat), transmises à OUTC-T1 vers 06:30. Ce sont des informations, sans alerte sauf mention : 1. Processus 7z (`7z.exe`, `7za.exe`, `7zG.exe`), avec le chemin et l'heure de lancement, requête faite dans CopieEtat. La sortie par défaut d'UeState reste inchangée. Un 7z dont la ligne de commande cite la copie donne l'alerte `PROC`. 2. `git status --porcelain` de tout l'arbre du jeu, séparé hors Docs / Docs (`jeu_hors_docs= jeu_docs=`). 3. HEAD court et branche des 3 dépôts (`head=//`). 4. Dernier verrou, lu sans écrire (`verrou=:`, avec `qui.txt` s'il est occupé). 5. Charge CPU (`cpu=%`, moyenne de `Win32_Processor.LoadPercentage`). 6. UeState et FileCheck : versions préparées hors place, puis remplacées en une seule écriture (ils servent à d'autres lots pendant OUTC). 7. Fiche OUTC-T6 : `Tools\EtatDir.ps1`. Un `-EtatDir` relatif ne crée plus de dossier parasite, et la règle `OUT[A-Z]?\d*` est ajoutée (`OUTC-T6-IMPL.md`). Ajouts à la VAL : V3 vérifie que les éléments 1 à 5 apparaissent dans la synthèse et dans le JSON. V12 : points P1 à P3 de `OUTC-T6-IMPL.md`. Tours : sans objet (fiche de l'orchestrateur)