# 21. Migration sandbox → addon éditeur (ébauche, 2026-10-06)
**Objectif :** extraire le code de sandbox Pair / Interaction / Mesure (aujourd'hui dans `Source/LifePlay/` et
`Source/LifePlayEditorTools/`, en grande partie non suivi par git) vers un **plugin éditeur** dédié, sans casser le
jeu empaqueté ni la chaîne d'animations (`20_Chaine_Animations.md`). Le module `LifePlay` ne garde que le contrat
minimal vers le jeu (dont `LPShowCoupleSubsystem`).
**Amont :** lot S0 validé (`S0-VAL.md` : PASS). Inventaire gelé : `Docs/Migration/etat/sandbox/S0/S0-CART.md`.
---
## Périmètre S0 (fichiers gelés)
Liste figée au 2026-10-06 ; toute évolution ultérieure passe par une fiche de lot et une entrée `CHG`.
### Runtime (`Source/LifePlay/`)
| Chemin | LOC approx. | Notes |
|---|---:|---|
| `LPPairSandbox.h` | 322 | Stage / Character / GameMode Pair |
| `LPPairSandbox.cpp` | 4261 | Monolith Pair, interaction, mesure |
| ~~`LPInteractionSandbox.*`~~ | — | **Retiré S4-T3** (addon) ; Interaction via menu `LPPairSandbox` |
| `LPPoseMeasure.h` | 100 | API mesure |
| `LPPoseMeasure.cpp` | 1558 | Sample / Transfer / T-fit |
| `LPSkeletonProfile.h` | 38 | Profils JSON |
| `LPSkeletonProfile.cpp` | 157 | |
| `LPPoseOrchestrator.h` | 30 | |
| `LPPoseOrchestrator.cpp` | 487 | |
| `LPAnimRedirect.h` | 15 | Cible `sandbox` \| `game` |
| `LPAnimRedirect.cpp` | 24 | |
| `LPShowCoupleSubsystem.h` | 77 | **Reste dans LifePlay** (sync lot S5) |
| `LPShowCoupleSubsystem.cpp` | 1048 | |
### Éditeur (`Source/LifePlayEditorTools/`)
| Chemin | LOC approx. | Notes |
|---|---:|---|
| `LPPairCollisionTestCommandlet.*` | ~546 | PairCol headless |
| `LPAnimEvalCommandlet.*` | ~489 | Mesure headless |
| `LPAnimCatalogCommandlet.*` | ~388 | |
| `LPAnimChecks.*` | ~435 | |
| `LPAnimTwistCommandlet.*` | ~384 | |
### Lanceurs (`Tools/debug/`)
| Chemin | Notes |
|---|---|
| `LifePlay_pair_sandbox.bat` | Pair |
| `LifePlay_interaction_sandbox.bat` | Interaction |
| `LifePlay_measure_sandbox.bat` | Mesure |
**Synthèse S0-CART :** ~7,5 k LOC runtime sandbox + mesure ; trois lanceurs désormais versionnables (motif
`.gitignore` : `/Debug/`).
---
## Contrat (S1 : configuration des chemins)
Le lot S1 fixe le premier contrat jeu ↔ addon : **tous les chemins d'assets et de fichiers du sandbox Pair passent par
`Config/LPSandbox.ini`**, lu par un point d'accès unique. Détail : `etat/sandbox/S1/S1-SPEC.md` ; implémentation
`S1-IMPL.md` ; audit `S1-CART.md` ; validation `S1-VAL.md` (PASS statique ; compilation et runtime à confirmer).
- **Fichier** : `Config/LPSandbox.ini` (versionné, pas dans `Saved/` ; depuis DC3 il vit dans l'addon, avec sa section `[Workshop]` `GameRoot`), lecture directe non hiérarchique (`FConfigFile::Read`).
- **API** : `struct LIFEPLAY_API FLPSandboxConfig` (`Source/LifePlay/LPSandboxConfig.h/.cpp`), `static const FLPSandboxConfig& Get()`, chargé une fois. `LPPairSandbox.cpp` et `LPAnimRedirect::ClipPath` n'utilisent plus de littéral `/Game/...`.
- **Repli** : fichier, clé ou valeur vide absents => default = littéral historique (comportement identique sans ini). Un asset introuvable garde le journal `LogLifePlay` d'origine, sans repli silencieux.
- **19 clés, 6 sections** : `Sandbox.Mode` (`Mode` = `Pair` | `Interaction` | `Mesure`, `Saved/sandbox_mode.txt` reste prioritaire) ; `Sandbox.Assets` (`PersonBP`, `BridgetMesh`, `MichaelMesh`, `QuaterniusMannequinMesh`, `DazIdleFemale`, `DazIdleMale`, `FurnitureWallMaterial`) ; `Sandbox.Roots` (`FurnitureRoot`, `DazAnimRoot`, `QuaterniusRoot`, `QuaterniusUALRoot`) ; `Sandbox.Content`, chemins relatifs à `Content/` (`ModuleRoot`, `WorldFile`, `ChatAction`, `QuaterniusUALDisk`) ; `Sandbox.Sky` (`SkyCubemap`, `SkyIntensity`) ; `Sandbox.Output`, relatif à `Saved/` (`MeasureOutputRoot`).
- **Conventions** : asset = chemin UE complet `/Game/...Nom.Nom` (`LoadObject`) ; racine = package sans nom d'asset ; disque = relatif à `ProjectContentDir()` (ou `ProjectSavedDir()` pour `Output`).
- **Hors contrat (volontaire)** : `LPKeyboard.ini`, `Saved/sandbox_mode.txt` / `sandbox_tour.txt`, littéraux `/Engine/...`, noms de clips et poses (`Idle_Loop`, `Walk_Loop`, `A_TPose`, `B_`/`M_`), liste `Names[]`.
- **Reste ouvert (lots suivants)** : points d'accroche ShowCouple (S5) ; staging de `LPSandbox.ini` si le sandbox doit tourner empaqueté. (Dépendances `Build.cs` et répartition des exports : traitées en S2, section suivante.)
---
## Module `LPAnimMeasure` (S2 : extraction de la chaîne de mesure)
Le lot S2 sort la chaîne de mesure de `LifePlay` vers un module Runtime dédié, `Source/LPAnimMeasure/` (structure plate, comme `LifePlay`). Détail : `etat/sandbox/S2/S2-CART.md` (cartographie), `S2-IMPL-H.md` (implémentation), `S2-VAL.md` (validation : PASS statique et runtime Mesure).
- **Contenu** : `LPPoseMeasure`, `LPSkeletonProfile`, `LPPoseOrchestrator`, `LPAnimRedirect` (8 fichiers déplacés) + `LPAnimMeasure.Build.cs`, `LPAnimMeasure.h/.cpp` (catégorie de journal `LogLPAnimMeasure`, ex-`LogLifePlay` pour les lignes `POSET` / `PROFIL` / `ORCH` / `MESURE`).
- **Exports** : `LPANIMMEASURE_API` remplace `LIFEPLAY_API` ; 6 symboles ajoutés pour `LPPairSandbox.cpp` (`KeyBones`, `LogReport`, `TFitReport`, `Run`, `Restore`, `SampleAt`) ; les structs de données restent header-only.
- **Rupture du couplage** : `LPAnimRedirect` ne lit plus `LPSandboxConfig`. l'addon (`LPSandbox.cpp:17`) appelle `LPAnimRedirect::SetUALRoot(FLPSandboxConfig::Get().QuaterniusUALRoot)` (`FLifePlayModule::StartupModule()` ne l'appelle plus) ; depuis DC1, `LPAnimRedirect` et `LPCoupleSim` sont dans le plugin du jeu `Plugins/LPAnimCore` ; défaut interne `/Game/Quaternius/UAL` (identique à celui de la config).
- **Déclaration** : entrée `Runtime` / `Default` dans `LifePlay.uproject`, placée avant `LifePlay` ; `ExtraModuleNames` des deux `*.Target.cs`.
### Graphe de dépendances des modules
```mermaid
flowchart LR
LPET["LifePlayEditorTools
(Editor)"] -->|Public| LP["LifePlay
(Runtime, module primaire)"]
MT["LPModTools
(Editor, addon, DC2)"] -->|Public| AM["LPAnimMeasure
(Runtime, nouveau S2)"]
LP -->|Public
LPPairSandbox, LPDirectControl| AM
LP -->|Public DC1| AC["LPAnimCore
(plugin du jeu : LPAnimRedirect, LPCoupleSim)"]
AM -->|Private DC1| AC
SB["LPSandbox
(addon)"] -->|DC1| AM
SB -->|DC1| AC
SB -.->|"LPSandbox.cpp:17 : SetUALRoot(QuaterniusUALRoot)"| AC
AM -->|Public| ENG["Core, CoreUObject, Engine"]
AM -->|Private| JSON["Json"]
```
Règles : `LPAnimMeasure` ne dépend ni de `LifePlay` ni de `LifePlayEditorTools` (graphe acyclique) ; `LPSandboxConfig` reste dans `LifePlay` et pousse la configuration vers `LPAnimMeasure` par un setter, jamais l'inverse. Ordre de chargement `LPAnimMeasure` avant `LifePlay` : dépendance `Build.cs` et ordre du `.uproject`.
| De | Vers | Type | Motif |
|---|---|---|---|
| `LifePlay` | `LPAnimMeasure` | Public | `LPPairSandbox.h/.cpp`, `LPDirectControlSubsystem.cpp`, `LifePlay.cpp` |
| `LifePlay` | `LPAnimCore` | Public | DC1 : `LPAnimRedirect`, `LPCoupleSim` (plugin du jeu `Plugins/LPAnimCore`) |
| `LPAnimMeasure` | `LPAnimCore` | Private | DC1 : la mesure utilise `LPAnimRedirect` |
| `LPSandbox` | `LPAnimMeasure`, `LPAnimCore` | Public | DC1 : mesure et redirection / géométrie du couple |
| `LifePlayEditorTools` | `LifePlay` | Public | existant |
| `LPModTools` (Editor, addon) | `LPAnimMeasure` | Public | DC2 : `LPAnimChecks.h` expose `FLPPose` ; commandlets `LPAnimEval`, `LPAnimCatalog`, `LPAnimTwist`, `LPAnimIdentify`, `LPHairFromStaticMesh` (ex-`LifePlayEditorTools`, qui ne dépend plus de `LPAnimMeasure`) |
| `LPAnimMeasure` | `Core`, `CoreUObject`, `Engine` / `Json` | Public / Privé | animations, squelettes ; lecture de `Tools/anim/profiles/*.json` |
| `LPAnimMeasure` | `LifePlay`, `LifePlayEditorTools` | **aucune** | évite un cycle de modules |
---
## Stratégie git et liaison jeu ↔ addon (S3)
Le lot S3 déplace le code sandbox dans un dépôt séparé, `LifePlay-Sandbox` (https://github.com/edujiro5972/LifePlay-Sandbox.git, branche `main`). Détail : `etat/sandbox/S3/S3-INT.md` ; validation : `etat/sandbox/S3/S3-VAL.md` (PASS).
**Décision : deux dépôts siblings + `AdditionalPluginDirectories`. Pas de submodule imbriqué.**
```
D:\Game\
LifePlay-master 5.5\ <- jeu (repo LifePlay)
LifePlay-Sandbox\ <- addon (repo LifePlay-Sandbox)
```
- **Liaison** : `LifePlay.uproject` déclare `"AdditionalPluginDirectories": ["../LifePlay-Sandbox/Plugins"]` (chemin relatif au `.uproject`) et active `LPSandbox` dans `Plugins`. UBT et l'éditeur y trouvent `LPSandbox.uplugin` (modules `LPSandbox` et `LPAnimMeasure`) ; les DLL du plugin sont compilées avec le jeu (`Build.bat LifePlayEditor Win64 Development`).
- **Pourquoi pas un submodule** : l'addon est un `uproject` mince qui contient le plugin sous `Plugins/LPSandbox/`. Un submodule monté sur `Plugins/LPSandbox` du jeu donnerait `Plugins/LPSandbox/Plugins/LPSandbox/` (plugin imbriqué, non scanné au bon niveau) ; un submodule `External/LifePlay-Sandbox` dupliquerait l'addon à côté du sibling et rendrait ambigu l'exemplaire chargé. Un clone sibling évite les deux problèmes.
- **Pin** : le jeu consomme un commit précis de l'addon, consigné à la main ici depuis le lot U0 (`etat/` est local ; auparavant dans `S3-INT.md`) (aujourd'hui `f0af432fd88965c92e3d01ddf2feb6194ce0adc4`, U1 commandlet `LPAuditBlueprintCalls` dans `LPModTools` et skills `lp-*` des sous-agents ; précédents : `79f0205b7281152dde7cd088086d8c813e0b20f5` DC3 atelier de mods (scripts, `ModSources`, `AnimSources`) dans l'addon, `97c0d134cd1388c3735add79d77ea98817e89fbe` DC2 module éditeur `LPModTools` et chemins propres à l'addon, `3e9c32f1f27b1abdaf7731dc90496c27ee061718` DC1 `LPAnimRedirect` et `LPCoupleSim` rapatriés dans le plugin `LPAnimCore` du jeu, `16905c68070992f5139dab907b41b461fb765957` SB4 ouverture par déclencheur éphémère non mémorisée, `3d858527fccfdddd8711c7c7448fa001442e0b8a` SB2-O1 correction de `ShowRoom`, `fece7e9a0e63b3058b5d24d96d669a3c9c0e9add` SB2 bibliothèques de scène et de personnages, caméra du socle, état des modes sorti vers les mods, `82dae58380b76c45f60541fd7cb5a0a409700e10` SB1 registre de mods et barre latérale, `3b21fd180c04f0fa49a1b5c9ef1b517ade1cb362` SB0 conception de la sandbox modulaire, `f09c42e5a2bc613cf269cce76c878461ca41c8b7` S7-T2 uplugin, `c1fdd29e2c1036c7d6365ba6389eff5d2f8c8c96` S5-T2 `LPCoupleSim`, `f8102647ccdebad37e4977ccbf2c51e583823a80`, S4-T3). Le pin est mis à jour (hash ici et dans le README de l'addon) à chaque évolution de l'addon que le jeu doit prendre. Reproduire :
```powershell
cd D:\Game
git clone https://github.com/edujiro5972/LifePlay-Sandbox.git
git -C LifePlay-Sandbox checkout f0af432fd88965c92e3d01ddf2feb6194ce0adc4
# le jeu doit être cloné à côté : D:\Game\LifePlay-master 5.5
```
- **Content** : le jeu n'a rien à monter. L'addon utilisé seul (`LPSandbox.uproject`) a besoin du Content du jeu : junction NTFS `LifePlay-Sandbox\Content` → `LifePlay-master 5.5\Content` (`/Game` ne peut pointer que vers `/Content` ; la clé `AdditionalAssetDirectories` n'existe pas en UE 5.5). Voir le README de l'addon.
- **Contenu du jeu après S3-T3** : `Source/LPAnimMeasure/` et les fichiers `LPPairSandbox*`, `LPInteractionSandbox*`, `LPSandboxConfig*` ne sont plus dans le jeu. Le bat Pair lance toujours `LifePlay.uproject` avec `game=/Script/LPSandbox.LPPairSandboxGameMode`. `Source/LifePlay/LifePlay.Build.cs` dépend toujours de `LPAnimMeasure` (module désormais fourni par le plugin).
- **Validation S3-VAL** : bat Pair depuis le jeu, plugin monté et GameMode résolu sans erreur de module ; addon mince lancé avec le même GameMode (réserve : son `.uproject` n'active pas `StreetMap`, d'où des erreurs de chargement de Blueprints du jeu sans effet sur le sandbox).
---
## CoupleBox : `LPCoupleSim` partagé (S5)
Le lot S5 ferme la **dérive de fork** entre la sandbox (`LPSandbox/LPCoupleSim.cpp`, méthodes de `ALPPairSandboxStage`) et le jeu (`LPShowCoupleSubsystem`) : les deux gardaient une copie de la géométrie du couple (mesure de pose, boîte, balayage, soudure) qui divergeait (plancher −90 / −88, itérations 2 / 3, os exclus). Détail : `etat/sandbox/S5/S5-CART.md` (écarts), `S5-IMPL-H.md` (implémentation), `S5-VAL.md` (validation : PASS runtime Pair, PASS-STATIC ShowCouple). Addon pinné sur `c1fdd29`.
- **Où** : header `Plugins/LPSandbox/Source/LPAnimMeasure/LPCoupleSim.h` (215 l.) et `LPCoupleSimCore.cpp` (474 l.), module `LPAnimMeasure`, `namespace LPCoupleSim`, exports `LPANIMMEASURE_API`. Une seule copie : le jeu la voit par `LifePlay.Build.cs` (dépendance publique existante), sans recopie ni changement de `.Build.cs`. Le nom `LPCoupleSimCore.cpp` évite la collision avec `LPSandbox/LPCoupleSim.cpp`.
- **Trois niveaux d'API** :
- *pur* (sans `UObject` ni `UWorld`, testable sans monde) : `MeshToActor`, `ComposeComponentSpace`, `AccumulatePoints`, `IsExcludedLiveBone`, `FitFromAnimBounds`, `FitFromLiveBounds`, `SlideWish` ;
- *lecture* : `CapsuleHalfOf`, `AccumulateAnimBounds` (8 pas, os animés, `GetBoneTransform(raw=false)`), `AccumulateLiveBody` ;
- *effets de bord* : `EnsureBox`, `ApplyBoxFit`, `PlaceBodyMesh`, `SaveAndFreeze` / `RestoreMove` (`FSavedMove`), `AttachPartner`, `Unweld`, `ResolveCoupleMove`.
- **Paramètres** : `FBoxFitParams` (demi-capsule 88, pad anime 12/12/6 plafond 800, pad vivant 4/4/4 min 8 plafond 400, ancre pelvis 140, `FloorLocalZ`, `MinExtentZ` 20) ; `FSweepOptions::Show()` (3 itérations, `SweepMulti`, dépénétration, PNJ + coque `Wall*` ignorés) et `FSweepOptions::Sandbox()` (2 itérations, `SweepSingle`, arrêt net, marches, Z à plat). `ResolveCoupleMove` **ne déplace rien** : elle rend la position retenue (`FSweepResult`), chaque appelant l'applique.
- **Appelants** : sandbox `FitCoupleBox` / `Weld` / `MoveCouple` ; jeu `FitBox` / `FitToBodies` / `Weld` / `Engage` / `Release` / `KeepTogether`. Les copies `AddPose` (jeu), `Accumulate` (sandbox), les deux créations de `UBoxComponent` et les deux boucles de balayage n'existent plus (−336 lignes dupliquées, +689 dans le module partagé).
- **Écarts conservés volontairement** (options, pas de copies) : plancher sandbox −90 contre jeu −88 (`FloorLocalZ`), balayage simple 2 itérations contre multi 3 itérations, nom de composant `CoupleBox` contre `LPShowCoupleBox`. L'alignement du plancher est une ligne dans `FitCoupleBox`, à décider après capture en jeu.
- **Reste dans `LifePlay`** : sélection du couple (`SexPersons`, `AP1/AP2`, `ShowReach`), réflexion `GI` / `Room`, `IsBedMesh` (injecté via `IsWalkableSupport`), `IsUseFurniture`, `OpenUseFurniture`, `PairSibling` / `IsIdleClip`, `FlipResponse` / `RestoreResponses` (non partagés, hors S5-T2), CVar `lp.ShowCouple`.
- **Contrat de modification** : tout changement de géométrie du couple se fait dans `LPCoupleSim` (addon), puis bump du pin ; le jeu ne doit pas réintroduire de copie locale.
---
## Acceptation
État au 2026-10-06 (lot S8). Case cochée = prouvée par la fiche citée ; case vide = non faite.
- [x] **S0** : hygiène dépôt, inventaire gelé (`S0-VAL.md` PASS).
- [x] **S1** : tous les chemins du sandbox via `Config/LPSandbox.ini` (`S1-VAL.md` PASS statique).
- [x] **S2** : module `LPAnimMeasure` extrait, sans dépendance vers `LifePlay` (`S2-VAL.md` PASS).
- [x] **S3** : addon dans le dépôt `LifePlay-Sandbox`, liaison sibling + `AdditionalPluginDirectories`, pin manuel (`S3-VAL.md` PASS).
- [x] **S4** : sandbox Pair découpé, GameMode Interaction legacy retiré (`S4-VAL.md` PASS).
- [x] **S5** : géométrie du couple partagée via `LPCoupleSim`, une seule copie (`S5-VAL.md` PASS runtime Pair, PASS-STATIC ShowCouple).
- [x] **S6** : stack Mesure sur la liaison addon, `LPAnimEval` court (`S6-VAL.md` PASS).
- [x] **S7 / isolation** : plugin sans `include` / `LIFEPLAY_API` / module `LifePlay` ; `Person` = CDO seul, aucun `GI` / `Room` ; chemins par config (`S7-SPEC.md`, `S7-CART.md` : 0 écart).
- [x] **S7 / configurations** : `LPSandbox` hors Shipping (`TargetConfigurationDenyList`), `LPAnimMeasure` conservé, plugin `Optional` dans `LifePlay.uproject` (`S7-IMPL-L.md`).
- [x] **Smoke Pair** (bat court, plugin monté, boîte du couple identique à S5-VAL, 0 crash) (`S7-VAL.md`).
- [x] Commandlets anim restants (`LPAnimCatalog`, `LPAnimChecks`, `LPAnimTwist`) déplacés dans un module éditeur de l'addon : **fait au lot DC2** (module `LPModTools`, avec `LPAnimEval`, `LPAnimIdentify`, `LPAnimMethodSelect`, `LPHairFromStaticMesh`).
- [x] **S8** : bilan du mode multi-agents (`22_Eval_Agents_Sandbox.md` : GO conditionnel) ; code sandbox absent du module jeu depuis S3 ; smoke Pair par le `.bat` réel, boîte du couple identique à S5 / S7, 0 crash (`S8-VAL.md` PASS).
- [ ] `BuildMod.ps1 -Target sandbox -Open` rejoué sur la liaison addon : **non fait**.
- [ ] Compilation / packaging **Shipping** complets avec le plugin (seul le plan UBT est vérifié).
- [ ] Parcours Show complet en jeu et décision d'alignement du plancher (-90 / -88).
- [x] Commit addon `f09c42e` poussé vers `origin/main` (constaté en S8 : `origin/main` = `dae6206`, qui le contient).
- [ ] Migration commitée **côté jeu** (`LifePlay.uproject`, `Config/LPSandbox.ini`, `Build.cs`, `LPShowCoupleSubsystem.*`, docs `21` / `22`, `etat/sandbox/`) : **non fait** (`22` § 6).
- [x] **SB0** : conception de la sandbox modulaire (addon, pin `3b21fd1`, tag `lot-SB0-valide`).
- [x] **SB1** : registre de mods, barre latérale, 7 descripteurs `.lpmod`, modes Pair / Interaction / Mesure enveloppés en `lps_couple` / `lps_interaction` / `lps_asset_integration` (`etat/sandbox/SB1/SB1-VAL.md` PASS, contrôle visuel utilisateur 2026-10-07 ; tag `lot-SB1-valide`).
- [x] **SB2** : bibliothèques `lps_lib_scene` / `lps_lib_actors`, caméra du socle, état des modes sorti vers les mods `lps_couple` / `lps_interaction` / `lps_asset_integration` (`etat/sandbox/SB2/SB2-VAL.md` PASS, contrôle visuel utilisateur 2026-10-07 ; tag `lot-SB2-valide`).
- [x] Reports SB1 (registre unique, lot propriétaire **SB2**) : (traité) images de la tournée Mesure non déterministes : SB2-T5 compare les JSON/CSV (identiques) et les images visuellement ; (traité, SB4-T0, prouvé SB4-VAL F2) « Open réécrit » : chaque ouverture de mod réécrit `Open` dans `Saved/sandbox_mods.ini` (une tournée automatique laisse donc `Open=lps_asset_integration`). Corrigé : `Open(Id, Context, bRemember)`, une ouverture par `Saved/sandbox_mode.txt` n'est plus mémorisée.
- [x] **SB4** : validation finale (SB4-VAL PASS : démarrage, tournées Quaternius et FreeAnimationsPack identiques à SB2-REF, LPAnimEval identique, Open intact), doc 23 § 7 ; commit `16905c6` et tag `lot-SB4-valide` ; évaluation `etat/sandbox/SB4/SB4-EVAL.md` : GO transposition.
- [x] **G-SB3** : SB3 abandonné (décision utilisateur 2026-10-07). Motif : les mods du jeu sont du contenu (.lpmod + pak), leurs dépendances sont vérifiées par le registre (prouvé en SB1/SB2) ; coût ≈ 1 à 1,5 lot, et `LPAnimMeasure` est utilisé par le jeu en Shipping ; on passe à SB4.
- [x] **SB2-O1** : correction du défaut O1 (`etat/sandbox/SB2/SB2-O1-BUILD.md` PASS, contrôle visuel utilisateur 2026-10-07 ; tag `lot-SB2-O1-valide`).
- [x] Reports SB2 : (O1) traité (SB2-O1, `3d858527fccfdddd8711c7c7448fa001442e0b8a` : `ShowRoom` symétrique de `HideRoom`, contrôle visuel utilisateur 2026-10-07 « c'est bon ») ; (E3) sans `lps_lib_scene`, la pièce est construite quand même : non traité, séparation physique abandonnée à la porte G-SB3 (décision utilisateur 2026-10-07).
- [x] **DC0** : cartographie et SPEC du découplage jeu / sandbox (`etat/sandbox/DC0/DC0-SPEC.md`, approuvée par l'utilisateur 2026-10-07) ; plan `24_Plan_Decouplage_Sandbox.md`.
- [x] **DC1** : Plugin LPAnimCore dans le jeu, LPAnimMeasure réduit à la mesure (DC1-T1..T5) (DC1-VAL PASS, contrôle visuel utilisateur 2026-10-07 ; tag addon lot-DC1-valide).
- [x] **DC2** : Commandlets de mods vers un module éditeur de la sandbox (DC2-T1..T5) (DC2-VAL PASS ; tag addon lot-DC2-valide ; le jeu compile et démarre sans l'addon).
- [x] **DC3** : Scripts, données et sources de mods vers la sandbox (DC3-T1..T5) (DC3-VAL PASS ; tag addon lot-DC3-valide ; atelier de mods dans l'addon).
- [x] **DC4** : Preuve « jeu seul », docs, évaluation (DC4-T1..T4) (DC4-VAL PASS, test en jeu utilisateur 2026-10-07 ; évaluation DC4-EVAL : le jeu public compile et tourne sans l'addon).
- [ ] Intégration des déplacements dans le jeu à améliorer (remarque utilisateur au contrôle visuel DC1, 2026-10-07) ; lot propriétaire : à définir, hors DC.
- [x] Reports DC0 (lot propriétaire **DC1**, traité : texte corrigé en § Rupture du couplage) : § Rupture du couplage : la mention d'un appel à SetUALRoot par FLifePlayModule::StartupModule() est périmée ; seul l'addon l'appelle (LPSandbox.cpp:17, LPSandboxMeasure.cpp:39) — voir DC0-CART b2.
- [x] Report DC2 (traité en DC3 : `-Livraison=` obligatoire) : `LPAnimCatalog` ignore `-DryRun` et a des valeurs par défaut (pack quaternius) : lancé sans arguments, il réécrit `Saved/AnimPipeline/quaternius/fiches_calculees.json` (DC2-VAL anomalie 1) — lot propriétaire DC3.
- [x] Report DC2 (traité en DC4, `fbbcee1`) : `LifePlay.Build.cs` garde `Json` avec un commentaire AP1 périmé (plus aucun usage dans `LifePlay`) — lot propriétaire DC4.
- [x] Report DC2 (traité : documenté dans `09`/`18`) : sans addon, l'absence est silencieuse au runtime ; l'éditeur/UBT affiche `AdditionalPluginDirectory … not found` (avertissement, non bloquant) — pour information, DC4 docs.
- [x] Report DC3 (traité : documenté dans `09`/`18`) : après un test sans addon, UBT réutilise un `Makefile.bin` sans les modules de l'addon : le déplacer avant de recompiler (DC3-BUILD) — à écrire dans `09`/`18` — lot propriétaire DC4.
- [x] Numéros de docs en double : traité (DOCNUM, CHG-246) — `23_Video_Quality.md` → `25_Video_Quality.md`, `24_Eval_Agents_VideoQuality.md` → `26_Eval_Agents_VideoQuality.md` (ordre de création ; 23 et 24 inchangés).
## Lots (S0 … S8)
| Lot | Objet (une ligne) |
|---|---|
| **S0** | Hygiène dépôt : `/Debug/` dans `.gitignore`, lanceurs `Tools/debug` suivis, inventaire et preuves `etat/sandbox/S0/*`. |
| **S1** | Contrat jeu ↔ addon : API minimale, dépendances modules, règles de linking (placeholder ci-dessus). |
| **S2** | Extraction du module `LPAnimMeasure` (mesure, profils, orchestrateur, redirection) hors de `LifePlay` ; dépendances `Build.cs` et exports. **Fait** : `S2-VAL.md` PASS (voir section « Module `LPAnimMeasure` »). Le plugin éditeur (uplugin) reste à faire. |
| **S3** | Création du dépôt addon `LifePlay-Sandbox` (scaffold, transfert du code, retrait côté jeu) et liaison par clone sibling + `AdditionalPluginDirectories`, pin manuel. **Fait** : `S3-VAL.md` PASS (voir section « Stratégie git »). Rejeu stack Mesure : lot **S6** (`S6-VAL.md`). |
| **S4** | Sandbox Pair : découpage `LPPairSandbox`, retrait GM Interaction legacy (S4-T3 **fait**), commandlet `LPPairCollisionTest`, bat Pair. Validation : `S4-VAL.md` (PASS). |
| **S5** | ShowCouple : `LPShowCoupleSubsystem` reste dans `LifePlay` ; géométrie du couple partagée via `LPCoupleSim` (module `LPAnimMeasure` de l'addon, pin `c1fdd29`). **Fait** : `S5-VAL.md` PASS (runtime Pair, PASS-STATIC ShowCouple ; voir section « CoupleBox »). Show complet en jeu et alignement du plancher restent à valider. |
| **S6** | Stack Mesure sur la liaison addon : `LifePlayEditorTools` → `LPAnimMeasure` (plugin), lanceurs `Tools/debug`, sorties `Saved/Measurement`, commandlet `-run=LPAnimEval`. **Fait** : `S6-VAL.md` PASS (doc `20` / `09`, essai court LPAnimEval). |
| **S7** | Isolation du plugin (sans lien vers `LifePlay`, `Person` en CDO seul, config seulement), `LPSandbox` hors Shipping, `Optional` dans `LifePlay.uproject`. **Fait** : `S7-VAL.md` PASS (smoke Pair ; Shipping vérifié par plan UBT). Le déplacement des commandlets Catalog / Checks / Twist reste à planifier (voir § Acceptation). |
| **S8** | Bilan et clôture : évaluation du mode multi-agents + multi-modèles (`22_Eval_Agents_Sandbox.md`, GO conditionnel, 3 recommandations), smoke Pair final. **Fait** : `S8-VAL.md` PASS. `BuildMod -Target` et commit côté jeu restent ouverts (§ Acceptation). |
Chaque lot suit `10_Protocole_Operations.md` : instantané, entrée `CHG`, retour arrière documenté.