Fiche OUTC-T2 — Niveau N1 — Modèle prévu sonnet | utilisé claude-sonnet-5-5 — Agent lp-impl — Skills sk-protocol (préchargé), sk-windows-shell (invoqué), sk-repo § 2 et § 6 et lp-repo § 7 (lus par Read), ue-cpp-build / lp-build / lp-ue-compat (préchargés, sans objet) # OUTC-T2-IMPL : `Tools\Verrou.ps1` et `Tools\Numero.ps1` (2026-10-11) Résultat : réussi, avec **un écart de moyen à décider** (§ 2 E1 : création du dossier de verrou par `CreateDirectoryW` et non par `New-Item`, parce que `New-Item` sans `-Force` n'est pas atomique, mesure à l'appui). Essais : 124 appels et contrôles, 0 écart (`SYNTHESE` de `OUTC-T2_essais.txt` l. 417) ; vrai `VERROU` lu en `-Etat` seulement ; vrai `NUMEROS.md` jamais écrit. ## 1. Fichiers modifiés (chemins, lignes) Dépôt du jeu `D:\Game\LifePlay-master 5.8`, branche `ue5.8`, HEAD `ae5d5e4` (la fiche citait `06dfb55` : le HEAD a avancé, sans effet sur ces fichiers). Les deux sont neufs, LF, sans BOM, ASCII seul (`FileCheck.ps1` : `LF`, `CRLF=0`, `BOM -`, alertes 0 ; octets > 0x7F = 0 ; analyse syntaxique pwsh : 0 erreur). | Fichier | Lignes | Taille | MD5 | Contenu | |---|---|---|---|---| | `Tools\Verrou.ps1` | 228 | 11 010 o | `788B21F30155200A497C3919FB3CBA29` | en-tête d'aide l. 1-58 ; `param` l. 59 ; `New-DossierAtomique` l. 75 ; `Get-VerrouK` l. 86 ; `Get-Qui` l. 99 ; `Fin` l. 123 ; `New-LignesOccupe` l. 130 ; `-Prendre` l. 157 ; `-Rendre` l. 190 ; `-Etat` l. 216 ; `catch` général (code 2) l. 225 | | `Tools\Numero.ps1` | 291 | 13 849 o | `8803E6A42DAD21292DFE3024732BF589` | en-tête d'aide ; `param` l. 63 ; `Get-MaxFichier` l. 85 ; `Get-MaxDocs` l. 100 ; `Get-MaxRegistre` l. 116 ; `Get-Maxima` l. 134 ; usage l. 168 ; `-Dernier` l. 178 ; `-Reserver` l. 194 ; `try` l. 220 ; `catch` l. 270 ; `finally` (rend le verrou) l. 273 | Comportement livré (conforme à `OUTC-SPEC.md` § 3.2 et § 3.3 sauf § 2) : - `Verrou.ps1 -Prendre [-Session] | -Rendre [-Ticket] | -Etat`, `-Racine` (défaut `\Docs\Migration\etat\REPO\VERROU`), `-Json`. Codes 0 / 1 / 2 comme la SPEC. Rien n'est supprimé ni renommé, aucun paramètre de forçage ; au-delà de 30 min, suffixe `ANCIEN (signaler, ne pas forcer)`. - `Numero.ps1 -Reserver CHG|ERR|DOC -Lot -Demandeur [-Nombre] [-Copie55] [-Fichier] [-Racine] [-Json]` et `-Dernier CHG|ERR|DOC`. Écriture par ajout d'octets (`FileStream` en `Append`), séparateur détecté, contrôle du préfixe (MD5 des `n` octets d'avant), de la longueur et de la fin ; verrou rendu dans un `finally`. ## 2. Écarts à la SPEC (obligatoire) - **E1 (à décider) — moyen de création du dossier de verrou.** SPEC § 3.2 point 2 et fiche § 5 prescrivent `New-Item -ItemType Directory` sans `-Force`, « échoue si le dossier existe ». Vrai en séquentiel, **faux en concurrence** : essai en 100 tours de 8 threads alignés sur une `Threading.Barrier`, tous appelant `New-Item -ItemType Directory -Path -ErrorAction Stop` (pwsh 7.6.6) : **exactement 1 succès dans 12 tours, plusieurs succès dans 88 tours**, 0 tour sans succès (sortie de la commande, tour 9 de cette fiche ; commande rappelée ci-dessous). Même essai avec `CreateDirectoryW` de `kernel32` (P/Invoke par `Add-Type`) : **1 succès dans 100 tours sur 100** ; second appel séquentiel : `False`, `GetLastWin32Error` = 183 (`ERROR_ALREADY_EXISTS`) (`scratchpad\OUTC-T2\stress2.ps1`, sortie dans ce compte rendu). Avec `New-Item`, deux gardiens pourraient donc se croire titulaires du même `k+1`, ce que le verrou doit empêcher. **Choix fait** : `New-DossierAtomique` (`Verrou.ps1` l. 75-84) appelle `CreateDirectoryW` ; 183 = occupé (code 1), autre erreur = code 2. Effet de bord : `Add-Type` coûte ≈ 640 ms au premier appel de chaque processus. Retour à la lettre de la SPEC : remplacer le corps de cette seule fonction par `try { New-Item -ItemType Directory -Path $Path -ErrorAction Stop | Out-Null; $true } catch { if (Test-Path -LiteralPath $Path) { $false } else { throw } }` ; je ne le recommande pas. Preuve en processus séparés (A17) : 5 tours de 6 processus `Verrou.ps1 -Prendre` lancés ensemble : à chaque tour un seul code 0, cinq codes 1, `qui.txt` du dossier = celui du gagnant (l. 184-204 de `OUTC-T2_essais.txt`). Commande de l'essai `New-Item` (non rangée en fichier, rejouable) : `pwsh -NoProfile -Command` avec `foreach ($r in 1..100) { $p = Join-Path $base "r$r"; $bar = [Threading.Barrier]::new(8); $res = 1..8 | ForEach-Object -Parallel { $b = $using:bar; $path = $using:p; $b.SignalAndWait(); try { New-Item -ItemType Directory -Path $path -ErrorAction Stop | Out-Null; 1 } catch { 0 } } -ThrottleLimit 8; ... somme des 1 par tour }`. - E2 — course simulée : paramètre caché `-TestCourse` (`[Parameter(DontShow)]`, décrit dans l'aide) : juste avant la création, crée le dossier cible comme le ferait un tiers (avec `qui.txt` `TIERS-TEST` pour `-Prendre`). Prévu par la fiche (« paramètre caché de test, à décrire »). - E3 — affichage d'un verrou occupé : première ligne `qui.txt (k=) : `, dernière ligne `VERROU OCCUPE k= par depuis min`, où `` = `qui.txt` sans la date finale. `-Etat` n'imprime que la ligne de verdict. La SPEC dit « affiche `\qui.txt` » sans préciser le format. Refus de `-Rendre -Ticket` : `VERROU REFUS k= : tenu par , pas par ` (libellé non fixé par la SPEC). - E4 — `-Rendre -Ticket T` : correspondance = `qui.txt` commence par `T` **suivi d'un caractère autre que lettre, chiffre, `_` ou `-`** (ou fin de ligne), insensible à la casse ; la SPEC dit « commence par ce ticket », ce qui ferait rendre le verrou de `OUTC-R10` à `OUTC-R1` (essais A11b, A11c). - E5 — points laissés ouverts par la SPEC, tranchés : sans `-Session`, `qui.txt` porte `session non precisee` ; `-Ticket` sans `-Rendre` et `-Session` sans `-Prendre` = paramètres incohérents, code 2 ; racine vide = `k` 0, libre ; `-Rendre` sans `-Ticket` rend le verrou tenu, quel qu'en soit le titulaire (SPEC § 3.2 point 3 ne demande le contrôle qu'avec `-Ticket`). - E6 — `-Json` accepté par les trois actions de `Verrou.ps1` (la SPEC ne le cite que pour `-Etat`) : `Numero.ps1` en a besoin pour lire `k` et le code. - E7 — `Numero.ps1`, format : date `AAAA-MM-JJ HH:MM` (heure locale ; la SPEC montre `AAAA-MM-JJ`, la fiche § 5 prescrit `Get-Date -Format 'yyyy-MM-dd HH:mm'`, comme les lignes existantes) ; plusieurs numéros : `NUMERO RESERVE CHG-330 CHG-331 lot=TEST verrou=5` ; JSON : `type`, `numeros[]` (entiers), `ids[]` (`CHG-330`), `lot`, `demandeur`, `verrou`, `fichier`, `date`, `maxima{copie58, copie55, registre, max}`, `avertissements[]` (la SPEC liste `{type, numeros[], lot, verrou, maxima{}}`, ces ajouts sont rétrocompatibles) ; `-Dernier` : `DERNIER CHG max=327 5.8=326 5.5=297 registre=327`. - E8 — cas non couverts par la SPEC : source 5.8 absente ou sans numéro = code 2 (jamais « max = 0 ») ; copie 5.5 absente = avertissement et `5.5=absent` dans la ligne ; `-Nombre` hors 1..50 = code 2 ; `|` du lot ou du demandeur remplacé par `/`, sauts de ligne par un blanc ; session du verrou = demandeur tronqué à 80 caractères, ticket `NUMERO-` ; fichier vide ou sans aucun saut de ligne = code 2 **après** la prise du verrou (le verrou est rendu : essais B6a, B6b) ; fichier à fins de ligne mixtes = avertissement, séparateur du dernier saut de ligne retenu ; verrou non rendu après une réservation réussie = ligne `VERROU NON RENDU` et code 2. - E9 — le livrable `OUTC-T2_essais.txt` demandé par la fiche est ignoré par git (`.gitignore:75 /Docs/Migration/etat/**`, seuls `.md` et `.csv` sont repris, l. 77) : il reste sur disque, hors « Fichiers à commiter ». ## 3. Instantané (lot, manifeste) Lot `OUTC` : `D:\Game\LifePlay-master 5.8` instantané `D:\Game\LifePlay_Backup_5.5\OUTC\manifest.csv` (2026-10-11 06:26) ; les deux fichiers y sont `Existed=False`, `Size=0` (lu l. 3-4 du manifeste) : rien à restaurer, le repli est « ne pas commiter / retirer les deux fichiers neufs » (`Tools\Restore.ps1 -Lot OUTC`, proposé, non lancé). ## 4. Ce qui n'a pas été fait - Aucun essai en écriture sur le vrai `VERROU` ni sur le vrai `NUMEROS.md` (interdit). Le vrai verrou n'a été lu que par `Verrou.ps1 -Etat` (A18, série officielle : `VERROU LIBRE k=93`, 186 dossiers, liste identique avant / après l'appel) et par `Get-ChildItem`. Le vrai `NUMEROS.md` n'a été lu que par `Numero.ps1 -Dernier` (B1r) et `Get-FileHash` ; MD5 `CD7800199276A0AB4F2019D4044816DF` au début, à la copie et à la fin de la série. - Pas d'essai de concurrence **entre processus** pour `Numero.ps1` (fait pour `Verrou.ps1`, A17) : l'exclusion repose sur `Verrou.ps1`. - Windows PowerShell 5.1 : 6 commandes seulement (§ D du fichier d'essais : `Verrou` `-Etat`, `-Prendre`, occupé, `-Rendre -Ticket` ; `Numero` `-Dernier`, `-Reserver`), codes attendus, puis `FileCheck` de la copie sous pwsh 7 ; pas la série complète. - Pas d'essai d'erreur de droits d'écriture ni de fichier verrouillé par un autre processus. - Pas de mise à jour de `sk-repo` § 2 ni de `lp-repo` § 7 (qui décrivent encore `mkdir` et la lecture manuelle) : lot DOC séparé (SPEC § 5, décision E5). - Aucun autre fichier de `Tools\` touché ; pas de commit, pas de git en écriture, aucune suppression. ## 5. Points à confirmer en VAL - E1 : l'orchestrateur confirme `CreateDirectoryW` (recommandé) ou demande le retour à `New-Item` ; la VAL peut rejouer `scratchpad\OUTC-T2\stress2.ps1 -Base ` (attendu : `exactement 1 succes=100`). - V7 rejoué : Prendre (0), Prendre encore (1), Etat (1), Rendre -Ticket autre (1), Rendre (0), Rendre encore (1), Etat (0) = essais A2 à A8 (`essais.ps1` du scratchpad, `-Base` neuf obligatoire : le script refuse une base où `VERROU` existe). - V7 course : `-TestCourse` rend 1 pour `-Prendre` et pour `-Rendre`, le `qui.txt` du tiers n'est pas écrasé (A12, A13). - V8 rejoué : `Numero.ps1 -Reserver CHG -Lot TEST -Fichier -Racine ` deux fois puis `-Nombre 2` : numéros consécutifs 328, 329, 330-331 ; préfixe de 12 601 octets de MD5 identique ; `FileCheck` : `CRLF`, `LF=0`, `CR=0` (B2, B4) ; verrou occupé = code 1, MD5 entier inchangé (B5). - Le format de la ligne ajoutée (statut `réservé` avec accents UTF-8 ; date avec heure) convient au gardien et à `lp-repo` § 7. - `-Rendre` sans `-Ticket` (E5) : acceptable pour le gardien, ou exiger `-Ticket`. - Coût de ≈ 640 ms d'`Add-Type` par appel de `Verrou.ps1` acceptable (≈ 1 085 appels de verrou en 72 h d'après la SPEC). - À la DOC (T5), `Numero.ps1 -Reserver CHG` réel sans `-Fichier` ni `-Racine` : le défaut est le vrai `NUMEROS.md` et le vrai `VERROU` ; à ce jour (06:44) le registre réel donne `max=327` (`CHG-326` et `CHG-327` posés par d'autres sessions depuis la rédaction de la fiche). ## 6. Fichiers à commiter Dépôt du jeu `D:\Game\LifePlay-master 5.8`, branche `ue5.8` : - A `Tools/Verrou.ps1` - A `Tools/Numero.ps1` - A `Docs/Migration/etat/OUTC/OUTC-T2-IMPL.md` Non commités : `Docs/Migration/etat/OUTC/OUTC-T2_essais.txt` (ignoré, `.gitignore:75`) ; scratchpad `\OUTC-T2\` (`essais.ps1`, `stress2.ps1`, racines et copies de test `VERROU*`, `NUMEROS-test.md`, `NUM-*.md`, essais de mise au point `dev1`, `dev2`, `stress`, `stress2`, sorties `dev1.txt`, `dev2.txt` ; rien supprimé). `essais.ps1` est réutilisable par la VAL : à ranger dans `Saved\OUTC\` ou à copier, selon `sk-protocol` § 7. ## Table des essais (commande → code → sortie) Sorties brutes complètes : `D:\Game\LifePlay-master 5.8\Docs\Migration\etat\OUTC\OUTC-T2_essais.txt` (76 appels `>>>` / `<<<`, dont 7 de la section D lancés à la main hors du script, + 55 contrôles `CHECK`, tous OK ; les 124 de `SYNTHESE` = 69 appels comptés par le script + 55 contrôles). Racine de test `…\scratchpad\OUTC-T2\VERROU` (départ `1 1-libre 2 2-libre`, k = 2). Toutes les commandes sont `pwsh -NoProfile -File