18 KiB
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=<copie>\Content\Modules |
nom qui finit par 5.5 |
D:\Game\LifePlay-Sandbox |
--git-dir=D:\Game\LifePlay_ModulesGit\5.5.git --work-tree=<copie>\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 <LOT> -Assets <chemins> [-Copie <racine>] [-Reference <json>] [-Autorise <chemins>]
[-Addon <racine>] [-ModulesGitDir <git-dir>] [-Json] [-Sortie <fichier.json>]
-Assets: chemins relatifs à la copie (ex.Content\LifePlay\GI.uasset) ; 0 à n.- Contrôles, dans cet ordre :
- Processus : appelle
Tools\UeState.ps1 -Copie <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). AlertePROCsi au moins un processus de la copie. - Assets : appelle
Tools\FileCheck.ps1 <assets> -ProjectRoot <copie> -Json; MD5, taille, date. AlerteASSET_ABSENT(introuvable). Avec-Reference(JSON d'une sortie précédente de CopieEtat, typiquement celle deBUILD-DEMANDE) : alerteASSET_MODIFIEsi le MD5 diffère, sauf si l'asset figure dans-Autorise. Avec-Lotet un manifesteD:\Game\LifePlay_Backup_5.5\<LOT>\manifest.csvprésent : colonneinstantane(identique / modifie / pas sauvegarde) dans le JSON, sans alerte (information). - Git :
git status --porcelain=v1 --untracked-files=allsur : jeu-- Content Config Source; addon-- Config Content Source Plugins/LPSandbox/Source Plugins/LPSandbox/Content; Modules (tout le dépôt). AlerteGITpour 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. - Parasites : sous
<copie>\Docs\Migration\etat\, tout dossier nomméetatouMigrationà n'importe quelle profondeur, et tout dossierDocsqui contient un sous-dossierMigration(ex.etat\Docs\Migration\etat\…,etat\U58\etat\…). Un dossierDocssansMigrationn'est pas un parasite :etat\publication\PUB2\neutral\Docs\Moddingest une copie de publication légitime (relevé du 2026-10-11,find -type d). AlertePARASITEpar dossier trouvé (le plus haut seulement, pas ses descendants). Parcours limité aux dossiers (pas de lecture de fichiers).
- Processus : appelle
- Sortie console : une ligne de synthèse en premier :
COPIE <OK|ALERTE> lot=<LOT> copie=<nom> proc=<n> assets=<n> absents=<n> modifies=<n> git=<jeu>/<addon>/<modules> hors_declares=<n> parasites=<n> alertes=<liste|aucune>puis une ligne par alerte (ALERTE <type> : <détail>). Avec-Json: le JSON seul sur la sortie standard (champssynthese,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-FINet relu par-Reference. - Codes : 0 aucune alerte ; 1 au moins une alerte ; 2 usage, copie, dépôt ou
-Referenceintrouvable. - 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 <racine>: 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 champcompte, sans texte autour.-WaitExitcombinable avec-Copie(attente des seuls processus de la copie).FileCheck.ps1 -Json: les lignes (Fins, CRLF, LF, CR, BOM, MD5, Sauv, Contenu, Alerte, Fichier) plusTailleetModifie(date ISO) et les comptes, en JSON seul ; codes inchangés.
3.2 Tools\Verrou.ps1 (OUTC-T2)
.\Tools\Verrou.ps1 -Prendre <ticket> [-Session <texte>] [-Racine <dossier VERROU>]
.\Tools\Verrou.ps1 -Rendre [-Ticket <ticket>] [-Racine …]
.\Tools\Verrou.ps1 -Etat [-Json] [-Racine …]
Racine par défaut : <projet>\Docs\Migration\etat\REPO\VERROU. Algorithme sk-repo § 2 à l'identique :
k= plus grand numéro parmi les sous-dossiers<k>et<k>-libre(numérique, pas alphabétique).-Prendre:<k>sans<k>-libre→ occupé : affiche<k>\qui.txt,VERROU OCCUPE k=<k> par <qui> depuis <min> min, code 1. Sinon[IO.Directory]::CreateDirectoryest interdit (il réussit si le dossier existe) : création parNew-Item -ItemType Directorysans-Force(échoue si le dossier existe) ; échec → occupé, code 1 ; réussite → écrit<k+1>\qui.txtau format en usage (<ticket>, session <session>, AAAA-MM-JJ HH:MM, une ligne, LF), afficheVERROU PRIS k=<k+1> ticket=<ticket>, code 0.-Rendre: le verrou tenu est<k>sans<k>-libre; aucun →VERROU DEJA LIBRE k=<k>, code 1. Avec-Ticket: siqui.txtne commence pas par ce ticket → refus, code 1 (on ne rend pas le verrou d'un autre). SinonNew-Item <k>-libre(même règle),VERROU RENDU k=<k>, code 0.-Etat:VERROU LIBRE k=<k>(code 0) ouVERROU OCCUPE k=<k> par <qui> depuis <min> min(code 1) ; au-delà de 30 min, suffixeANCIEN (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 <LOT> -Demandeur <texte> [-Nombre <n>] [-Copie55 <racine>]
[-Fichier <NUMEROS.md>] [-Racine <dossier VERROU>] [-Json]
.\Tools\Numero.ps1 -Dernier CHG|ERR|DOC # lecture seule, sans verrou : maxima par source
-Reserver: prend le verrou parVerrou.ps1 -Prendre NUMERO-<LOT>(même-Racine) ; occupé → code 1, rien écrit. Calcule le maximum entre : copie 5.8 (Docs\Journal_Modifications.mdmotif^\| CHG-(\d+);Docs\Registre_Erreurs.mdmotif^\| ERR-(\d+);Docs\NN_*.mdpour DOC), copie 5.5 (-Copie55, défautD:\Game\LifePlay-master 5.5:Docs\Migration\Journal_Modifications.md,Docs\Migration\Registre_Erreurs.md,Docs\Migration\NN_*.md; absente → avertissement, pas d'échec), etNUMEROS.md(colonne Numéro des lignes du type, y compris les plages058 à 086: la borne haute compte). Réservemax+1(oumax+1 … max+navec-Nombre, une ligne par numéro).- Ajoute à la fin du tableau de
NUMEROS.mdla ligne| CHG | 325 | <LOT> | <Demandeur> ; verrou <k> ; max lus : 5.8=<a> 5.5=<b> registre=<c> | AAAA-MM-JJ | réservé |(CHG et ERR sur 3 chiffres, DOC sur 2) en conservant les fins de ligne du fichier :NUMEROS.mdest 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 : lesnpremiers 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=<LOT> verrou=<k>;-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 <filtres>] [-Copie <racine>] [-Addon <racine>]
[-ModulesGitDir <git-dir>] [-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-Cheminsi donné :<depot> <XY> <name-status> <eol index>/<eol arbre> CR:<arbre>/<HEAD> <chemin>name-status: degit diff --name-status HEAD -- <chemin>(M,A,D,R100…,?pour non suivi) ;eol: degit ls-files --eol -- <chemin>(i/lf w/crlf…,-si non suivi ou supprimé) ;CR: nombre d'octets\rdans le fichier de l'arbre et dans le blobHEAD:<chemin>(git cat-file -p),-s'il n'existe pas ; binaire (octet nul dans les 8 000 premiers octets) →bin.
- Alerte
FINS(ligne suffixéeALERTE 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 <OK|ALERTE> jeu=<branche>@<head> <n> fichiers ; addon=… ; modules=… ; alertes_fins=<n>. -Tous: ajoute les fichiers suivis non modifiés pour les-Chemindonnés (contrôle d'EOL d'un fichier propre, ex.NUMEROS.md) ; sans-Chemin,-Tousest 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 pargrep -c $'\r$'(faux sous Git Bash, mémoireeol-check-etat-lf).
4. Repli, risques, questions
- Repli :
Tools\Restore.ps1 -Lot OUTC(proposé, jamais lancé sans accord) : retire les 4 scripts neufs et rendUeState.ps1etFileCheck.ps1d'avant (MD5A47107D5…,CA9CBA16…, manifesteD:\Game\LifePlay_Backup_5.5\OUTC). - R1 : régression de
UeState.ps1/FileCheck.ps1, appelés parBuild.ps1et 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.ps1testé sur le vraiVERROUpendant qu'un gardien travaille → tous les essais de VAL et d'IMPL sur une racine de test (-Racinedans le scratchpad), le vrai verrou seulement en-Etat(lecture). - R3 :
Numero.ps1qui casse le CRLF deNUMEROS.md→ essais sur une copie deNUMEROS.md(-Fichier), contrôle parFileCheck.ps1et 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 (-Copievers 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; sinonCopieEtatserait 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 <f> 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 <f> |
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 <copie de NUMEROS.md> -Racine <test> 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 :
- 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'alertePROC. git status --porcelainde tout l'arbre du jeu, séparé hors Docs / Docs (jeu_hors_docs=<n> jeu_docs=<n>).- HEAD court et branche des 3 dépôts (
head=<jeu>/<addon>/<modules>). - Dernier verrou, lu sans écrire (
verrou=<k>:<libre|occupe>, avecqui.txts'il est occupé). - Charge CPU (
cpu=<n>%, moyenne deWin32_Processor.LoadPercentage). - UeState et FileCheck : versions préparées hors place, puis remplacées en une seule écriture (ils servent à d'autres lots pendant OUTC).
- Fiche OUTC-T6 :
Tools\EtatDir.ps1. Un-EtatDirrelatif ne crée plus de dossier parasite, et la règleOUT[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)