# 22. Évaluation du mode multi-agents + multi-modèles (migration sandbox → addon, S0–S8) - **Date :** 2026-10-06 | **Fiche :** S8-T1 | **Rôle :** A-DOC+Orch | **Entrée journal :** CHG-220 - **Modèle prévu :** `claude-opus-5-5-medium` (M4) | **Modèle utilisé :** Claude Opus 5.5 (déclaré par l'environnement) - **Sources :** `etat/sandbox/S0/` à `S7/` (29 rapports `*-CART`, `*-SPEC`, `*-IMPL*`, `*-INT*`, `*-VAL`), `21_Sandbox_Addon.md`, journal `CHG-212` à `CHG-219`, état git des deux dépôts au 2026-10-06 21:00. - **Validation de clôture :** [`etat/sandbox/S8/S8-VAL.md`](etat/sandbox/S8/S8-VAL.md) (smoke Pair par le `.bat`, **PASS**). **Biais à garder en tête :** ce bilan est écrit par le rôle orchestrateur, qui juge en partie sa propre organisation. Les faits (verdicts, horodatages, état git) sont vérifiables dans les fiches citées ; les appréciations (sur/sous-classement, coût) sont des jugements. --- ## 1. Résumé exécutif **Fait.** Le code sandbox (Pair, Interaction, Mesure, ~7,5 k LOC) est sorti du module jeu vers le plugin `LPSandbox` du dépôt séparé `LifePlay-Sandbox`, chargé par le jeu via `AdditionalPluginDirectories` (clone sibling, pin manuel). La chaîne de mesure est un module à part (`LPAnimMeasure`), la géométrie du couple n'existe plus qu'en une copie (`LPCoupleSim`, −336 lignes dupliquées), le plugin ne connaît pas le jeu à la compilation (0 include / `LIFEPLAY_API`) et `LPSandbox` est exclu du Shipping. Huit validations PASS, aucune régression observée : la boîte du couple `AgMakeout` est identique au millième de cm entre S5, S7 et S8. Aucun retour arrière n'a été nécessaire. Le tout a été fait en une journée (S1 → S7 en ~3 h d'après les horodatages des fiches, 18:18 → 20:54). **Limites.** - **Rien n'est commité côté jeu** : `LifePlay.uproject`, `Config/LPSandbox.ini`, `21`, `etat/sandbox/**`, et `LPShowCoupleSubsystem.*` (non suivis). Un clone du jeu aujourd'hui ne monte pas l'addon ; le pin n'a de valeur que sur ce poste. - **Show en jeu jamais exécuté** : le partage `LPCoupleSim` côté jeu n'est prouvé que par compilation (PASS-STATIC, S5) ; l'alignement du plancher −90 / −88 n'est pas décidé. - **Shipping non compilé** (plan UBT seulement), commandlets anim (`Catalog`, `Checks`, `Twist`) toujours dans `LifePlayEditorTools`, `BuildMod -Target sandbox` non rejoué. - **Le build du jeu dépend du clone sibling** (`LifePlay.Build.cs` → `LPAnimMeasure`) malgré `Optional: true`. - Preuves par journal uniquement : aucun contrôle visuel, modes Interaction et Mesure non exercés après S2. --- ## 2. Lots S0–S8 « Rollback » = retour arrière **exécuté**. Tous les lots ont un retour arrière documenté dans le journal ; celui des lots S1, S2 et S5 côté jeu porte sur des fichiers non suivis par git, donc sans instantané : il est manuel et non testé. | Lot | Tenu | Coupé / reporté | Rollback | Verdict VAL | |---|---|---|---|---| | **S0** | `.gitignore` `/Debug/`, 3 bats suivis, inventaire gelé | — | aucun | **PASS** (`S0-VAL`) | | **S1** | `Config/LPSandbox.ini` (19 clés, 6 sections), `FLPSandboxConfig`, 0 littéral oublié | compilation et runtime (DLL plus ancienne que les sources) ; test d'override `WorldFile` laissé « manuel », jamais fait | aucun | **PASS statique** (`S1-VAL`, runtime BLOCKED) | | **S2** | module `LPAnimMeasure` (8 fichiers, 6 exports), graphe acyclique, runtime Mesure (269 lignes `LogLPAnimMeasure`) | commandlets non relancés (repris en S6 pour `LPAnimEval` seul) ; mise à jour de `S1-CART` demandée, **jamais faite** | aucun | **PASS** (`S2-VAL`) | | **S3** | dépôt addon, transfert, purge du jeu, `AdditionalPluginDirectories`, pin, build 91/91 | **submodule abandonné** ; addon mince avec réserve (plugin `StreetMap` absent, 2 458 erreurs BP sans effet) | **partiel** : la junction `Plugins\LPSandbox` créée en S3-T3 a été retirée en S3-T4 | **PASS** avec réserve (`S3-VAL`) | | **S4** | `LPPairSandbox.cpp` 4 274 → 7 unités (max 837 LOC), GameMode Interaction legacy retiré (~543 LOC) | commandlet `LPPairCollisionTest` cité dans l'objet du lot : non traité ; mode Interaction non exercé | aucun | **PASS** (`S4-VAL`) | | **S5** | `LPCoupleSim` partagé (3 niveaux d'API), 0 erreur / 0 avertissement, parité de la boîte | Show en jeu, décision du plancher, tests unitaires du niveau pur, mise sous git de `LPShowCoupleSubsystem` | aucun | **PASS** runtime Pair / **PASS-STATIC** Show (`S5-VAL`) | | **S6** | doc `20` / `09` alignées, `LPAnimEval` court (code 0, 1,3 s) | bat Mesure non rejoué (renvoi à S2-VAL) | aucun | **PASS** (`S6-VAL`) | | **S7** | contrat d'isolation (0 écart), `TargetConfigurationDenyList: Shipping`, `Optional` | compilation Shipping ; déplacement des commandlets ; push (fait après coup, constaté en S8) | aucun | **PASS** (`S7-VAL`) | | **S8** | ce bilan ; smoke Pair par le `.bat` réel, parité de la boîte | `BuildMod -Target`, bout en bout Show | aucun | **PASS** (`S8-VAL`) | --- ## 3. Qualité des consignes et des handoffs **Ce qui a marché.** - Chaque VAL cite ses références, le pin, le journal copié et une section « Limites » ; les verdicts nuancés (`PASS statique`, `PASS-STATIC`, `BLOCKED-RUNTIME`, « avec réserve ») ont empêché de sur-vendre un lot. - L'enchaînement CART → IMPL-H a payé : S3-T2, S4-T2 et S5-T2 ont compilé du premier coup, sans include à corriger (hors un défaut IWYU révélé par l'unity en S2). - Les écarts à la fiche sont déclarés au lieu d'être cachés : S4-T2 (7 unités au lieu de 5, justifié par la taille), S5-T2 (5 écarts de comportement listés exhaustivement), S7-T2 (options rejetées avec motif). **Ambiguïtés de consigne observées.** | Où | Ambiguïté | Effet | |---|---|---| | S1-T2 / S1-T4 | IMPL-L sans consigne de compiler, VAL sans droit de compiler | personne n'a compilé S1 ; le runtime n'a été prouvé qu'indirectement par S2 | | S3-T3 / S3-T4 | plan « submodule en T4 » alors que la structure de l'addon (plugin imbriqué) le rendait impossible | junction temporaire créée puis retirée, décision d'architecture prise en INT | | S3-T1, S7-T2 | « push non autorisé » sans dire qui pousse ni quand | pin pointant un commit local entre S7-T2 et la fin de S7 | | S4 | objet du lot dans `21` (commandlet `LPPairCollisionTest`) ≠ fiches exécutées | item tombé sans trace de décision | | S7 / S8 | libellés de `21` (S7 « commandlets », S8 « épuration, `BuildMod -Target` ») ≠ fiches reçues | S7-VAL doit signaler un « ancien libellé non traité » ; S8 ici = bilan, pas épuration | | `21` § Lots | ligne S1 toujours « placeholder ci-dessus » | doc de référence partiellement périmée | **Reprises.** S1 runtime → couvert par S2 ; commandlets S2 → `LPAnimEval` en S6 (les autres jamais) ; junction S3-T3 → retirée S3-T4 ; bat équivalent S7 → bat réel S8 ; push S7 → fait (constaté S8). **Orphelins** (demandés, jamais repris) : mise à jour `S1-CART`, test d'override `WorldFile`, Show en jeu, commit côté jeu. **Forme.** Les fiches S3-INT et S7 sont écrites sans accents et sans en-tête « Modèle », les autres avec : le gabarit de fiche n'était pas imposé. --- ## 4. Adéquation modèle ↔ complexité Paliers d'après les fiches : M0 = `composer-2.5-fast`, M2 = `claude-sonnet-5-5-high`, M3 = `claude-opus-5-thinking-high`, M4 = `claude-opus-5-5-medium` ; `gemini-3.8-flash-high` n'a pas de palier noté (M1 supposé). « Réel » = champ « Modèle utilisé » de la fiche ; sinon « non tracé dans VAL », et l'évaluation porte sur le prévu. **12 fiches sur 28 tracent le modèle réel (11 sur 27 avant S8).** | Fiche | Rôle | Modèle prévu | Réel | Résultat | Classement | |---|---|---|---|---|---| | S0-T1 | CART | gemini-3.8-flash-high | idem | inventaire exact | adéquat | | S0-T2 | IMPL (`.gitignore`) | non tracé | non tracé dans VAL | 1 ligne, PASS | — | | S0-T3 | VAL | composer-2.5-fast (M0) | idem | PASS, preuve `check-ignore` | adéquat | | S1-T1 | ARCH (SPEC) | non tracé | non tracé dans VAL | contrat solide, réutilisé jusqu'en S7 | — | | S1-T2 | IMPL-L | non tracé | non tracé dans VAL | correct mais **non compilé** | — (défaut de consigne, pas de modèle) | | S1-T3 | CART (audit grep) | gemini-3.8-flash-high | idem | 0 oubli, confirmé par VAL | adéquat | | S1-T4 | VAL | sonnet (M2) | idem | bon diagnostic (DLL > sources) mais checklist statique | **légèrement sur-classé** (M0 a fait des VAL plus lourdes en S4/S6) | | S2-T1 | CART | sonnet (M2) | non tracé dans VAL | cartographie fiable | adéquat | | S2-T2 | IMPL-H | opus thinking (M3) | non tracé dans VAL | compilé, 1 fix IWYU | **sur-classé** (déplacement + exports, M2 suffisait) | | S2-T4 | VAL | sonnet (M2) | idem | PASS runtime Mesure | adéquat | | S3-T1 | INT (scaffold) | sonnet (M2) | non tracé dans VAL | scaffold correct, push gelé | adéquat | | S3-T2 | IMPL-H | opus thinking (M3) | non tracé dans VAL | transfert inter-dépôts compilé du premier coup | adéquat | | S3-T3 | IMPL-L (purge jeu) | composer (M0) | composer (champ unique) | purge correcte, **sans build**, junction contraire à la décision finale | **sous-classé** (opération destructive inter-dépôts) | | S3-T4 | INT | non tracé | non tracé dans VAL | abandon du submodule, build 91/91, premier push | — | | S3-T5 | VAL | sonnet (M2) | idem | deux runtimes, cause `StreetMap` identifiée | adéquat | | S4-T1 | CART | non tracé | non tracé dans VAL | découpage exploitable | — | | S4-T2 | IMPL-H | opus thinking (M3) | non tracé dans VAL | 4 274 LOC découpés, 0 erreur | adéquat (risque élevé, aucune régression) | | S4-T3 | IMPL (retrait GM) | non tracé | non tracé dans VAL | suppression propre | — | | S4-T4a | VAL | composer (M0) | idem | build + runtime, PASS | adéquat | | S5-T1 | CART | sonnet (M2) | non tracé dans VAL | 28 Ko d'écarts précis, base de S5-T2 | adéquat | | S5-T2 | IMPL-H | opus thinking (M3) | idem | refonte de comportement, 0 avertissement, écarts listés | adéquat | | S5-T3 | VAL | sonnet (M2) | idem | parité argumentée, PASS-STATIC honnête | adéquat | | S6-T1..T3 | VAL (+DOC) | composer (M0) | idem | PASS, commandlet court | adéquat | | S7-T1 | ARCH (SPEC) | non tracé | non tracé dans VAL | checklist C1–C5 réutilisable | — | | S7-T2 | IMPL-L | non tracé | non tracé dans VAL | uplugin / uproject corrects | — | | S7-T3 | CART | non tracé | non tracé dans VAL | 0 écart | — | | S7-T4 | VAL+DOC | non tracé | non tracé dans VAL | PASS, limites claires | — | | S8-T1/T2 | DOC+Orch, VAL | opus 5.5 medium (M4) | Claude Opus 5.5 (effort non vérifiable) | ce document, smoke PASS | adéquat pour la synthèse ; M0 suffirait pour le smoke seul | **Lecture.** Aucun échec imputable au modèle. Les deux défauts notables viennent de la **consigne** (S1 sans build) et d'un **palier trop bas pour une opération destructive** (S3-T3). Le sur-classement (S2-T2, S1-T4) n'a rien cassé, il coûte seulement. M0 s'est montré suffisant pour toutes les VAL à checklist + smoke. --- ## 5. Erreurs d'affectation de rôle 1. **VAL qui écrit la doc qu'elle valide** : S2-T4b (« A-VAL puis prep DOC »), S6 (VAL a fait la mise à jour de `20` / `09` puis l'a validée), S7-T4 (« VAL+DOC », coche elle-même `21` § Acceptation). Pas d'erreur constatée, mais la séparation des rôles perd son intérêt. 2. **Décision d'architecture prise par INT** : l'abandon du submodule (S3-T4) relevait d'A-ARCH ; il a été pris en intégration, sans SPEC préalable, et documenté a posteriori dans `21`. 3. **Compilation sans propriétaire en S1** : ni IMPL-L ni VAL n'avaient le droit de compiler ; le trou a été comblé par chance au lot suivant. 4. **Purge destructive confiée à IMPL-L / M0 (S3-T3)** : suppression de fichiers du jeu et modification du `.uproject`, sans build dans la fiche. 5. **Orchestrateur évaluateur (S8-T1)** : le rôle qui a découpé les lots en fait le bilan ; voir le biais signalé en tête. --- ## 6. Friction git - **Submodule abandonné → `AdditionalPluginDirectories`.** Le plan initial (S3-T3 : « jonctions temporaires, avant submodule T4 ») supposait un submodule. L'addon étant un `uproject` qui contient `Plugins/LPSandbox/`, un submodule monté sur `Plugins/LPSandbox` du jeu aurait donné `Plugins/LPSandbox/Plugins/LPSandbox/` (non scanné), et un submodule `External/` aurait doublé le clone sibling. S3-T4 a retiré la junction et retenu deux clones voisins + `AdditionalPluginDirectories` + pin consigné à la main (`S3-INT.md`, README). Coût : une junction créée puis défaite, et un pin que rien n'automatise (4 bumps à la main : `4de1f0b`, `f810264`, `c1fdd29`, `f09c42e`). - **Push.** S3-T1 a créé le remote sans pousser (consigne) ; premier push en S3-T4. S7-T2 a laissé `f09c42e` local et le pin pointait un commit absent du remote jusqu'à la fin de S7. Constat S8 : `origin/main` = `dae6206` (contient `f09c42e`), **l'addon est à jour sur GitHub**. Côté jeu, en revanche, **aucun commit depuis le début du lot** : le dernier commit touchant `LifePlay.uproject` est antérieur (`b11f7e8`) ; `21`, `etat/sandbox/**`, `Config/LPSandbox.ini` sont non suivis, `LifePlay.uproject` et les deux `Build.cs` modifiés non commités. Le pin documenté n'est donc consommable par personne d'autre. - **Identité git.** Les deux premiers commits de l'addon (`4938abb` S3-T1, `d18243e` S3-T2) portent `LifePlay ` : identité non configurée dans le nouveau dépôt. Les suivants portent `edujiro5972 `, alors que le dépôt jeu utilise `336072401+edujiro5972@users.noreply.github.com`. Trois identités pour un seul auteur ; l'historique publié n'a pas été réécrit (il faudrait un push forcé). --- ## 7. Coût / bénéfice vs mono-agent mono-modèle **Coût mesurable.** - 27 fiches (S0–S7) et 29 rapports, ~200 Ko de Markdown sous `etat/sandbox/` (S5 seul : 52 Ko) pour un changement net de quelques milliers de lignes déplacées et −336 lignes dupliquées. - Chaque agent relit `21` et les fiches amont : le contexte est rechargé ~28 fois. - Trous de transmission que la continuité d'un seul agent aurait évités : S1 non compilé, junction S3 défaite, `S1-CART` jamais mis à jour, commit jeu jamais fait. - Traçabilité incomplète : 11 fiches sur 27 (S0–S7) indiquent le modèle réel. **Bénéfice mesurable.** - 0 build cassé à l'étape IMPL-H, 0 retour arrière, 0 régression (parité de la boîte S5 = S7 = S8). - Des VAL indépendantes qui ont refusé de sur-vendre : `BLOCKED-RUNTIME` en S1, `PASS-STATIC` en S5, réserve `StreetMap` en S3. Un agent unique valide plus volontiers son propre travail. - Les paliers bas (M0, flash) ont porté 5 fiches de VAL / CART sans défaut (S0-T1, S0-T3, S1-T3, S4-T4a, S6) ; un mono-modèle haut de gamme aurait payé ces fiches au prix fort. **Verdict.** Pour une migration à risque (déplacement inter-dépôts, refonte de géométrie partagée), le surcoût documentaire est justifié par l'absence de régression et la qualité des preuves. Pour des lots simples (S0, S6, S7-T2) le découpage en 3–4 rôles est du surcoût pur : un seul agent M0/M2 avec une VAL séparée suffisait. --- ## 8. Décision **GO conditionnel** pour réutiliser le mode multi-agents + multi-modèles sur les prochaines migrations de même nature (extraction de code, refonte partagée jeu ↔ addon). Les trois conditions sont les recommandations ci-dessous ; sans elles, les mêmes trous (build sans propriétaire, git non clos, traçabilité partielle) se reproduiront. **NO-GO** pour les lots simples (≤ 1 fichier de config / doc, pas de C++) : un agent unique + une VAL M0. --- ## 9. Recommandations (3) 1. **Politique modèles, tracée et vérifiée.** M0 (`composer-2.5-fast`) par défaut pour les VAL à checklist + smoke et les IMPL-L mécaniques non destructives ; `gemini-3.8-flash-high` pour les CART de type grep ; M2 (`claude-sonnet-5-5-high`) pour les CART analytiques, les VAL qui doivent juger une parité ou un PASS-STATIC, **et toute opération destructive ou inter-dépôts** (suppression de sources, `.uproject`) ; M3 (`claude-opus-5-thinking-high`) réservé aux IMPL-H qui changent un comportement (type S5-T2), pas aux déplacements (type S2-T2 → M2) ; M4 pour la synthèse transverse. Chaque fiche porte « Modèle prévu | utilisé » ; une VAL sans ce champ dans les fiches amont est en `FAIL-TRACE`. 2. **Clôture git par lot, avec un propriétaire.** Toute fiche IMPL finit par un build vert (la compilation appartient à IMPL, pas à VAL). Le lot ne passe au suivant qu'après un « gate » A-INT : commit + push de l'addon, bump du pin, **commit du jeu** (fichiers non suivis inclus), identité git vérifiée (`git config user.email` identique dans les deux dépôts). Prochaine action concrète : commiter côté jeu `LifePlay.uproject`, `Config/LPSandbox.ini`, les `Build.cs`, `LPShowCoupleSubsystem.*`, `21`, `22` et `etat/sandbox/`. 3. **Séparer VAL et DOC, et tenir un registre des reports.** La VAL est en lecture seule sur les docs d'acceptation ; un rôle DOC distinct coche `21`. Les décisions d'architecture passent par une SPEC avant l'INT. Chaque « reste à faire » d'une fiche entre dans une liste unique de `21` avec un lot propriétaire, pour qu'aucun report ne reste orphelin (aujourd'hui : `S1-CART`, override `WorldFile`, Show en jeu, plancher, commandlets, Shipping compilé).