272 lines
17 KiB
Markdown
272 lines
17 KiB
Markdown
Fiche CAM-B2b-F7 — Niveau N3 — Modèle prévu opus | utilisé claude-opus-5-5 (Opus 5.5) — Agent lp-mcp-bp — Skills sk-protocol, sk-change-safety, lp-mcp, lp-lot
|
||
# CAM-B2b-F7-IMPL : pilotage de C2 par `DownUp` (B2b-6, fin), relecture globale, compilation stricte, enregistrement de Room (2026-10-13)
|
||
|
||
Consignes : `CAM-B2b-FICHES.md` § « Rubriques communes » (l. 23-63) et § « F7 » (l. 134-142). Seul `DownUp` restait à
|
||
faire : `RotateCamera` a été traité en F6b. SPEC : `CAM-SPEC.md` § « CAM-B2b », ligne B2b-6 (l. 380). Modèle suivi :
|
||
`CAM-B2b-IMPL-F6b.md`, partie `RotateCamera` (même schéma).
|
||
|
||
Contexte : UE-GO de Big Boss B2b du 2026-10-13, cité par l'orchestrateur. Éditeur headless PID 10708, port 8000,
|
||
connexion `unreal-mcp` partagée (MCPA-T1). Room chargé, F1 à F6b en mémoire. Un seul `call_tool` par message.
|
||
**24 appels MCP** :
|
||
- 3 `get_node_infos`, 6 `create_node`, 1 `break_pins`, 9 `connect_pins` et 3 `set_pin_value` ;
|
||
- 1 `compile_blueprint` et 1 `save_assets`.
|
||
|
||
Aucun `delete_node`, `write_graph_dsl` ni `ProgrammaticToolset`. Aucune écriture par HyperAIStudio.
|
||
|
||
## 1. Fichiers modifiés
|
||
|
||
- `Content/BPs/Room.uasset` : **enregistré** par `save_assets` (§ 1.8). Il contient F1 à F7. MD5 avant
|
||
`a5fa970e…` (= instantané) ; après `e0517c63541f249bd9dbda5c39815ba5`. Taille passée de 6 060 745 à 6 318 319 octets.
|
||
- Graphe `/Game/BPs/Room.Room:EventGraph`, événement `DownUp` (CE8, x 5520, y 3952) : nœuds neufs en x 5700-6550,
|
||
y 4250-4550.
|
||
- Aucun autre asset n'a été ouvert en écriture ni enregistré (§ 3).
|
||
|
||
### 1.1 Lecture avant écriture (`get_node_infos` groupé, 3 nœuds existants)
|
||
|
||
La lecture est conforme aux faits de la fiche :
|
||
- `K2Node_CustomEvent_8` (`AddEvent|Custom|DownUp`) :
|
||
- `then` out 1 → `IfThenElse_8`.execute 0 ;
|
||
- `AxisValue` out 2 (`Float (double-précision)`) → `CommutativeAssociativeBinaryOperator_3`.0, `_5`.0 et `_8`.0.
|
||
- `K2Node_IfThenElse_8` : execute ← CE8.1 ; Condition ← `CommutativeAssociativeBinaryOperator_17`.0 ; then 0 →
|
||
`IfThenElse_17`.0 ; else 1 libre.
|
||
- `K2Node_PromotableOperator_8` (`Utilitaires|Equal(Object)`) : A ← `CallFunction_439`.0, B ← `VariableGet_268`.0 ; out 0
|
||
(`Booléen`) → `IfThenElse_91`.1, `_96`.1, `_97`.1, `_98`.1 et `_99`.1.
|
||
|
||
### 1.2 Nœuds créés (préfixe `refPath` : `/Game/BPs/Room.Room:EventGraph.`)
|
||
|
||
| Rôle | `refPath` (suffixe) | `type_id` demandé → relu | Broches relues | Position |
|
||
|---|---|---|---|---|
|
||
| **Branche** C2 | `K2Node_IfThenElse_100` | `Utilitaires\|ContrôleDeFlux\|Branche` → idem | execute 0, Condition 1 ; then 0, else 1 | 5750, 4250 |
|
||
| Obtenir C2Height | `K2Node_VariableGet_296` | `Variables\|Pardéfaut\|ObtenirC2Height` → `\|GetC2Height` | C2Height out 0 (double) | 5700, 4450 |
|
||
| AxisValue × 3 | `K2Node_PromotableOperator_35` | `Utilitaires\|Opérateurs\|Multiplier` → `Math\|Flottant\|float*float` | A 0, B 1 ; out 0 (double) | 5900, 4550 |
|
||
| C2Height + produit | `K2Node_PromotableOperator_36` | `Utilitaires\|Opérateurs\|Ajouter` → `Math\|Flottant\|float+float` | A 0, B 1 ; out 0 (double) | 6100, 4450 |
|
||
| Clamp | `K2Node_CallFunction_511` | `Math\|Flottant\|Clamp(Float)` → idem | Value 0, Min 1, Max 2 ; ReturnValue out 0 (double) | 6300, 4500 |
|
||
| **Définir C2Height** | `K2Node_VariableSet_71` | `Variables\|Pardéfaut\|DéfinirC2Height` → `\|SetC2Height` | execute 0, C2Height 1 ; then 0, Output_Get 1 | 6550, 4400 |
|
||
|
||
Comme en F3 à F6b, les opérateurs naissent avec un `type_id` trompeur et des broches `Caractère de remplacement` :
|
||
- PO35 : `Utilitaires|Gestiondutemps|Seconds*FrameRate` ;
|
||
- PO36 : `…|FrameNumber+Int`.
|
||
|
||
À la relecture finale, ils sont typés `float*float` et `float+float`, en double précision. À la création, le Clamp avait
|
||
Min `0.0` et Max `1.0`. PO8 est réutilisé sans copie (nœud pur).
|
||
|
||
### 1.3 Lien coupé (`break_pins`, 1, `{"returnValue":null}`)
|
||
|
||
| # | Sortie | Entrée |
|
||
|---|---|---|
|
||
| C1 | CustomEvent_8.then 1 | IfThenElse_8.execute 0 |
|
||
|
||
Aucun autre lien n'a été coupé, et rien d'autre n'a été touché dans `DownUp`.
|
||
|
||
### 1.4 Liens faits (`connect_pins`, 9, tous `{"returnValue":null}`)
|
||
|
||
| # | Sortie | Entrée |
|
||
|---|---|---|
|
||
| L1 | CustomEvent_8.then 1 | IfThenElse_100.execute 0 |
|
||
| L2 | IfThenElse_100.else 1 | IfThenElse_8.execute 0 |
|
||
| L3 | PromotableOperator_8.0 | IfThenElse_100.Condition 1 |
|
||
| L4 | CustomEvent_8.AxisValue 2 | PromotableOperator_35.A 0 |
|
||
| L5 | VariableGet_296.0 (`C2Height`) | PromotableOperator_36.A 0 |
|
||
| L6 | PromotableOperator_35.0 | PromotableOperator_36.B 1 |
|
||
| L7 | PromotableOperator_36.0 | CallFunction_511.Value 0 |
|
||
| L8 | CallFunction_511.0 | VariableSet_71.C2Height 1 |
|
||
| L9 | IfThenElse_100.then 0 | VariableSet_71.execute 0 |
|
||
|
||
`VariableSet_71.then` reste libre : c'est la fin de l'événement côté C2, comme dans F6a et F6b.
|
||
|
||
### 1.5 Valeurs (`set_pin_value`, 3, tous `{"returnValue":null}`, relues)
|
||
|
||
| Broche | Avant (relu à la création) | Après (relu) |
|
||
|---|---|---|
|
||
| PromotableOperator_35.B 1 | (neuf, `""`, joker) | `3.0` (`Float (double-précision)`) |
|
||
| CallFunction_511.Min 1 | `0.0` | `-40.0` |
|
||
| CallFunction_511.Max 2 | `1.0` | `150.0` |
|
||
|
||
`PO35.B` a été posé après L4, qui type l'opérateur. La Condition de la Branche 100 garde sa valeur par défaut `true`, sans
|
||
effet puisque la broche est liée (L3).
|
||
|
||
### 1.6 Relecture globale (`get_node_infos` groupé, 23 nœuds)
|
||
|
||
Nœuds relus : les 6 nœuds créés, `IfThenElse_8`, PO8, CE1, CE4, CE8, CE19, CE55, `CallFunction_459`, `_467`, `_474`,
|
||
`_499`, `_505`, `VariableSet_61`, `_62`, `_66`, `CustomEvent_49` et `CallFunction_494`. Chaque lien a été comparé au
|
||
rapport de la fiche qui l'a posé :
|
||
|
||
**DownUp (F7)**
|
||
- Les 9 liens L1 à L9 apparaissent des deux côtés, et les 3 valeurs sont relues.
|
||
- `IfThenElse_8` : execute ← `IfThenElse_100`.1 ; Condition ← `CommutativeAssociativeBinaryOperator_17` ; then →
|
||
`IfThenElse_17` ; else libre.
|
||
- CE8.AxisValue → CABO_3.0, _5.0, _8.0, plus PO35.0.
|
||
|
||
**PO8** : sortie → `IfThenElse_91`, `_96`, `_97`, `_98`, `_99` et `_100` (6 branches).
|
||
|
||
**Événements de déplacement (F6a, F6b)**
|
||
- CE1 `CircleCamera` : then → `IfThenElse_96` ; DeltaSeconds → CABO_26.1, _16.1, _27.1, plus PO28.1 (F6a L1, L5).
|
||
- CE4 `LeftRight` : then → `IfThenElse_97` ; AxisValue → CABO_47.0, _13.0, _24.0, plus PO29.0 (F6a L8, L11).
|
||
- CE19 `RotateCamera` : then → `IfThenElse_99` ; DeltaSeconds → CABO_45.1, _12.1, _15.1, plus PO33.0 (F6b L10, L13).
|
||
- CE55 `ScrollDownUp` : then → `IfThenElse_98` ; Values → CABO_2.0, CF_26.2, CABO_20.0, _10.0, _22.0, plus PO31.0
|
||
(F6b L1, L4).
|
||
|
||
**`ComputeC2Target` et `ComputeRoomBox` (F2, F3, F5)**
|
||
- 494 `|ComputeRoomBox` : execute ← CE47.1 ; then → 459.0 (F3 L1, L2).
|
||
- 459 (trace de `Desired`) :
|
||
- execute ← 494, then → `IfThenElse_90`, ReturnValue → `IfThenElse_90`.1, OutHit → 460 ;
|
||
- Start ← 455, End ← 495 (F3 L6), ActorsToIgnore ← `MakeArray_5` ;
|
||
- Radius `20.0` (F3), TraceChannel `TraceTypeQuery2`.
|
||
- `VariableSet_61` : execute ← `IfThenElse_90`.0 ; C2Target ← 496 (F3 L11) ; then → `IfThenElse_95` (F5 L1).
|
||
- `VariableSet_62` : execute ← `IfThenElse_90`.1 ; C2Target ← 495 (F3 L7) ; then → `IfThenElse_95` (F5 L2).
|
||
- 505 (trace du repli) :
|
||
- execute ← `IfThenElse_95`.0, then → `VariableSet_66`, OutHit → 506, ReturnValue → 507.2 ;
|
||
- Start ← 455, End ← 504, ActorsToIgnore ← `MakeArray_5` ;
|
||
- Radius `20.0`, `TraceTypeQuery2` (F5 L14 à L18, L24, L29).
|
||
- `VariableSet_66` : execute ← 505 ; C2Target ← 508 (F5 L28) ; then libre.
|
||
- CE49 `ComputeRoomBox` : then → `VariableSet_64` (F2 L21).
|
||
|
||
**Poursuite (F4)**
|
||
- 467 `|ComputeC2Target` : execute ← `IfThenElse_93`.1 ; then → 499 (F4 L1).
|
||
- 499 (trace de la poursuite) :
|
||
- execute ← 467, then → 474, OutHit → 500, ReturnValue → 501.2 ;
|
||
- Start ← 455, End ← 468, ActorsToIgnore ← `MakeArray_5` ;
|
||
- Radius `20.0`, `TraceTypeQuery2` (F4 L1 à L6, L12).
|
||
- 474 : execute ← 499 (F4 L2) ; self ← `VariableGet_268` ; NewLocation ← 502 `Safe` (F4 L16) ; NewRotation ← 469 ; bSweep
|
||
`false`.
|
||
|
||
**Aucune divergence.** Chaîne obtenue pour `DownUp` : CE8 → Branche 100 (`GetViewTarget(PC) == CamRoom`).
|
||
- Vrai : `Définir C2Height` (71) := `Clamp(C2Height + AxisValue × 3, −40, 150)`, puis fin.
|
||
- Faux : `IfThenElse_8`, chaîne d'origine inchangée.
|
||
|
||
### 1.7 Compilation
|
||
|
||
`compile_blueprint {blueprint:{refPath:"/Game/BPs/Room.Room"}, warnings_as_errors:true}` a renvoyé
|
||
`{"returnValue":null}`, sans exception : c'est le critère de la fiche.
|
||
|
||
Deux contrôles n'ont pas été faits :
|
||
- `hyper_blueprint_debug_inspect`, absent du serveur le 2026-10-10 (mémoire `unreal-mcp-reconnect`, non revérifié) ;
|
||
- le journal de l'éditeur, faute du chemin `-abslog` : il n'est pas dans `Saved\Logs`, dont la fin de la liste ne montre
|
||
que `UnrealPak*.log`, `cef3.log` et `portage`.
|
||
|
||
### 1.8 Enregistrement
|
||
|
||
`editor_toolset.toolsets.asset.AssetTools` / `save_assets {"asset_paths":["/Game/BPs/Room"]}` a renvoyé
|
||
`{"returnValue":true}`. Il a été appelé une seule fois, pour Room seul, après la compilation stricte.
|
||
|
||
## 2. Écarts à la SPEC
|
||
|
||
- **Aucun écart de câblage.** La ligne B2b-6 (`CAM-SPEC.md:380`) et la fiche (`CAM-B2b-FICHES.md:137`) sont réalisées
|
||
telles quelles pour `DownUp`. Seul le lien CE8.then → `IfThenElse_8` a été coupé.
|
||
- **Formule** : `C2Height := Clamp(C2Height + AxisValue × 3, −40, 150)`. A de PO36 = C2Height, B = produit, relu.
|
||
- **Typage** : `AxisValue` est en double précision, comme `Clamp(Float)` : aucune conversion implicite.
|
||
- **Relecture** : la liste de la fiche (l. 139) a été élargie, à la demande de l'orchestrateur, à 499, 505,
|
||
`VariableSet_66`, CE49 et 494, plus `IfThenElse_8` et PO8.
|
||
- **Garde `Accessed None`** : aucune `IsValid` ajoutée, car ni la fiche ni la SPEC n'en demandent, et les nœuds neufs ne
|
||
lisent que des flottants. PO8 (→ 439 `GetViewTarget`, self ← `VariableGet_262` ; → `VariableGet_268` `CamRoom`) est
|
||
désormais évalué à chaque appel de `DownUp`. Même réserve qu'en F6a et F6b. PO8 alimente maintenant 6 branches.
|
||
- **Comportement** : quand C2 n'est pas la vue, il est identique à l'ancien (`else` → `IfThenElse_8`). Quand C2 est la
|
||
vue, la chaîne d'origine **n'est plus exécutée** : c'est le changement voulu par la SPEC.
|
||
- **Positions** : choisies près de CE8, sans prescription (cosmétique). Elles chevauchent visuellement les nœuds
|
||
`CircleCamera` de F6a (x 5650-6050, y 4250-4450), sans effet sur le graphe.
|
||
|
||
## 3. Instantané
|
||
|
||
L'orchestrateur l'a fait avant F1 (`D:\Game\LifePlay_Backup_5.5\CAM-B2b\`) ; il n'a pas été refait, selon la consigne.
|
||
Mesures avant et après `save_assets` :
|
||
```
|
||
date -Iseconds; md5sum "/d/Game/LifePlay-master 5.8/Content/BPs/Room.uasset"; grep -i "Room.uasset" "/d/Game/LifePlay_Backup_5.5/CAM-B2b/manifest.csv"; git -C "D:\Game\LifePlay-master 5.8" status --short -- Content Source Config
|
||
2026-10-13T16:10:25+02:00
|
||
a5fa970e821dc2d0d9bf220fd43b9de6 */d/Game/LifePlay-master 5.8/Content/BPs/Room.uasset
|
||
"Content\BPs\Room.uasset","True","6060745","A5FA970E821DC2D0D9BF220FD43B9DE6","2026-10-13T15:25:45"
|
||
M Content/BPs/Person.uasset
|
||
M Content/BPs/Room.uasset
|
||
M Source/LifePlay/LPDebugSubsystem.cpp
|
||
M Source/LifePlay/LPDebugSubsystem.h
|
||
```
|
||
(save_assets → true)
|
||
```
|
||
date -Iseconds; md5sum "/d/Game/LifePlay-master 5.8/Content/BPs/Room.uasset"; stat -c '%s %y' "/d/Game/LifePlay-master 5.8/Content/BPs/Room.uasset"; git -C "D:\Game\LifePlay-master 5.8" status --short -- Content Source Config; md5sum "/d/Game/LifePlay-master 5.8/Content/BPs/Person.uasset"
|
||
2026-10-13T16:10:34+02:00
|
||
e0517c63541f249bd9dbda5c39815ba5 */d/Game/LifePlay-master 5.8/Content/BPs/Room.uasset
|
||
6318319 2026-10-13 16:10:30.045597500 +0200
|
||
M Content/BPs/Person.uasset
|
||
M Content/BPs/Room.uasset
|
||
M Source/LifePlay/LPDebugSubsystem.cpp
|
||
M Source/LifePlay/LPDebugSubsystem.h
|
||
b239e1035cb028b78fbd4bb9f0c5f70a */d/Game/LifePlay-master 5.8/Content/BPs/Person.uasset
|
||
|
||
stat -c '%n %s %y' ".../Content/BPs/Person.uasset" ".../Content/BPs/Room.uasset"
|
||
/d/Game/LifePlay-master 5.8/Content/BPs/Person.uasset 20808711 2026-10-13 14:35:14.930487700 +0200
|
||
/d/Game/LifePlay-master 5.8/Content/BPs/Room.uasset 6318319 2026-10-13 16:10:30.045597500 +0200
|
||
```
|
||
Lecture des mesures :
|
||
- Le MD5 de Room diffère de l'instantané `A5FA970E` : l'enregistrement a bien eu lieu.
|
||
- `Person.uasset` date de 14:35:14, avant B2b : ce n'est pas l'enregistrement de F7. `LPDebugSubsystem.*` viennent
|
||
d'autres lots ; aucun n'a été touché.
|
||
- Room figurait **déjà** en `M` avant l'enregistrement, avec le MD5 de l'instantané. L'instantané de 15:25 diffère donc
|
||
déjà de HEAD. Estimé : enregistrement d'un lot antérieur (B2) non encore commité. À trancher par le gardien avant le
|
||
commit.
|
||
|
||
Retour arrière, sur décision de l'orchestrateur et jamais lancé ici :
|
||
1. fermer l'éditeur ;
|
||
2. `.\Tools\Snapshot.ps1 -Lot CAM-B2b-retour -Path Content\BPs\Room.uasset` ;
|
||
3. `.\Tools\Restore.ps1 -Lot CAM-B2b -Only Content\BPs\Room.uasset -WhatIf`, puis sans `-WhatIf`.
|
||
|
||
Retour attendu à l'octet : MD5 `A5FA970E…`, 6 060 745 octets.
|
||
|
||
## 4. Ce qui n'a pas été fait
|
||
|
||
- `hyper_blueprint_debug_inspect` et journal de l'éditeur : non consultés (§ 1.7).
|
||
- `CompileAllBlueprints`, éditeur fermé : il revient à l'orchestrateur (`lp-mcp` § 1, règle 3).
|
||
- `IfThenElse_96`, `_97`, `_98`, `_99` n'ont pas été relus ici. Leurs `else` → `IfThenElse_26`, `_9`, `_10`, `_25` sont
|
||
établis par les relectures finales de F6a et F6b, pas par celle de F7. `CallFunction_473` (Start ← 502, F4 L17) n'a pas
|
||
été relu non plus.
|
||
- Comportement à l'exécution : non mesuré (aucune PIE, aucun build), seulement déduit du câblage.
|
||
- Preuve en jeu (build empaqueté + `LogStats`), V5 et V6 : hors rôle ; c'est la suite de l'orchestrateur.
|
||
- Fermeture de l'éditeur : non faite, selon la consigne. PID 10708 reste ouvert.
|
||
|
||
## 5. Points à confirmer en VAL
|
||
|
||
1. **Vue autre que C2** : `DownUp` prend `else` → `IfThenElse_8`, avec la chaîne d'origine inchangée (Condition ←
|
||
CABO_17, then → `IfThenElse_17`). Établi par le câblage ; à confirmer en jeu (hauteur de l'ancienne caméra identique).
|
||
2. **Vue C2** : `DownUp` ajoute `AxisValue × 3` à `C2Height`, borné à [−40, 150]. L'effet passe par `ComputeC2Target`
|
||
(F3) au prochain appel. Signe et gain sont à juger à l'essai.
|
||
3. **`LogStats`** : aucun `Accessed None` nouveau sur `CallFunction_439` / `VariableGet_262`. PO8 est évalué à chaque
|
||
`DownUp`, probablement à chaque image tant que l'axe est actif (estimé, appelant non lu).
|
||
4. **Cas limite (estimé, déjà noté en F6a et F6b)** : si `GetViewTarget` et `CamRoom` valent tous deux `None`, PO8 vaut
|
||
`true`, et `DownUp` écrit `C2Height` au lieu de piloter l'ancienne caméra.
|
||
5. **V5 (`CAM-SPEC.md:382`)** : pour les 5 événements, `then` → nouvelle branche et `else` → branche d'origine :
|
||
- CE1 → 96, `else` → 26 ;
|
||
- CE4 → 97, `else` → 9 ;
|
||
- CE19 → 99, `else` → 25 ;
|
||
- CE8 → 100, `else` → 8 ;
|
||
- CE55 → 98, `else` → 10.
|
||
|
||
Également : `SphereTraceSingle` 3 (459, 499, 505), `Vector_BoundedToBox` ≥ 4, `ComputeRoomBox` 1.
|
||
6. **Valeurs sérialisées**, à relire par dump (`LPDumpFunction`) sur le fichier enregistré :
|
||
- PO35.B = 3.0 ; 511 Min −40.0, Max 150.0 ;
|
||
- celles de F1 à F6b, déjà listées dans leurs IMPL (par exemple PO31.B 15.0, 509 [−75, 125], PO33.B 3.0,
|
||
510 [−40, 150], Radius 20 de 459, 499 et 505).
|
||
7. **Compilation** : jugée sur la seule absence d'exception de `compile_blueprint {warnings_as_errors:true}`. À
|
||
recontrôler par `CompileAllBlueprints`, éditeur fermé, avec 0 erreur et 0 avertissement nouveau sur Room.
|
||
8. **État git de Room** : il était déjà `M` avant F7 (§ 3). Le gardien doit établir ce que contient le diff par rapport à
|
||
HEAD (B2 et B2b ?) avant le commit.
|
||
|
||
## 6. Fichiers à commiter
|
||
|
||
Dépôt du jeu, branche `ue5.8` :
|
||
- `A Docs/Migration/etat/CAM/CAM-B2b-IMPL-F7.md`
|
||
- `M Content/BPs/Room.uasset`, après VAL (V5, V6), avec la réserve du point 8 du § 5.
|
||
|
||
`Content/BPs/Person.uasset` et `Source/LifePlay/LPDebugSubsystem.*` n'appartiennent pas à ce lot : ne pas les joindre.
|
||
|
||
Tours : 30 / 50 (ce Write et la remise du compte rendu compris ; 24 appels MCP, un par message)
|
||
|
||
## Constats
|
||
|
||
- Le schéma F6b se rejoue tel quel pour `DownUp` : 6 créations, 1 coupure, 9 liaisons et 3 valeurs, plus 2 lectures
|
||
groupées, soit 21 appels pour l'événement.
|
||
- Les index de broches sont identiques à F6b (Branche, `Clamp(Float)`, `|SetC2Height`) ; `|GetC2Height` → out 0.
|
||
- Dans le graphe, les numéros des nœuds créés suivent ceux de F6b : `IfThenElse_100`, `VariableGet_296`,
|
||
`PromotableOperator_35` et `_36`, `CallFunction_511`, `VariableSet_71`.
|
||
- `save_assets` sur `/Game/BPs/Room` renvoie `{"returnValue":true}` (booléen) ; les écritures de graphe renvoient
|
||
`null`.
|
||
- À verser : lp-mcp § 3 — `save_assets {asset_paths:["/Game/BPs/X"]}` (toolset `editor_toolset.toolsets.asset.AssetTools`)
|
||
renvoie `true`. Avant d'enregistrer, mesurer le MD5 et `git status` : un asset peut déjà être `M` du fait d'un lot
|
||
antérieur, ce qui change ce que le gardien commitera.
|