diff --git a/CLAUDE.md b/CLAUDE.md index d4b5d3f..5245e62 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -276,7 +276,25 @@ de surplus est `decision.params.budgetW`, pas `targetW`. Elle le restera tant qu pas livrée — **à lire comme l'exception documentée qu'elle est, jamais comme une divergence à diagnostiquer**. Cf. `LoadTelemetry.surplusAllocatedW` et `LoadTelemetryEntry.surplusShareW`. -**2. `allocatedW` est ce qui a été COMMANDÉ — jamais ce que la charge tire.** +**2. `allocatedW` est ce qui a été DÉCIDÉ — ni le commandé, ni ce que la charge tire.** +Corrigé le 2026-08-29 par le moteur : `buildTelemetry()` publie le **plan**, +`applyActionsToAdapters()` reçoit le slot en `const`, et rien n'est réécrit après le +dispatch — un test attend `allocatedW == 3000` sur une borne dont le courant commandé donne +2 990 W. **Trois nombres, jamais le même sens** : + +| Nombre | Champ | Ce qu'il dit | +|---|---|---| +| **Décidé** | `allocatedW` (et `Σ levels[].targetW`, identiques) | ce que l'arbitre a planifié — ce que les compteurs de budget comptent | +| **Appliqué** | `mechanism.*` (`stageW`, `setpointW`, `currentA`) | ce que l'adaptateur a su produire | +| **Mesuré** | `measurement.powerW` | ce que la charge tire, quand c'est mesurable | + +**décidé ↔ appliqué** = l'arrondi du mécanisme, normal et permanent. **appliqué ↔ mesuré** = +la charge n'obéit pas, et ce que l'écart prouve dépend de `measurement.source`. +**décidé ↔ mesuré** = la somme des deux, à ne jamais lire seule. + +> ⚠️ **Ne pas chercher l'arrondi entre `Σ targetW` et `allocatedW`** : ils sont égaux par +> construction. Un écran bâti là-dessus ne montrerait jamais rien, **et sans erreur**. + La mise en garde « pour une borne, ce n'est même pas une commande » est **levée** : depuis `+etm23`, `adjustEvChargers()` ne commande plus, l'arbitre est seul (mesuré : 9 commandes à la borne, 9 par l'arbitre, 0 par le proxy). Mais ce qui devient vrai, c'est diff --git a/lib/models/load_telemetry.dart b/lib/models/load_telemetry.dart index 5820cb0..9ce9d8a 100644 --- a/lib/models/load_telemetry.dart +++ b/lib/models/load_telemetry.dart @@ -583,10 +583,17 @@ class LoadTelemetryEntry { return total; } - /// **[allocatedW] est ce qui a été COMMANDÉ. Ce n'est pas ce que la charge tire.** + /// **[allocatedW] est ce qui a été DÉCIDÉ — ni le commandé, ni ce que la charge tire.** /// - /// La distinction a changé de nature avec le lot plugin 3g-2, et il faut la reprendre - /// exactement, sans l'élargir. + /// Corrigé le 2026-08-29 : `buildTelemetry()` publie le **plan**, + /// `applyActionsToAdapters()` reçoit le slot en `const`, et rien n'est réécrit après le + /// dispatch — un test du moteur attend `allocatedW == 3000` sur une borne dont le + /// courant commandé donne 2 990 W. D'où `Σ levels[].targetW == allocatedW`, une + /// **identité** et non un écart d'arrondi : l'arrondi vit après, dans l'adaptateur, et + /// se lit dans [mechanism]. + /// + /// La distinction avec ce que la charge TIRE a changé de nature avec le lot plugin + /// 3g-2, et il faut la reprendre exactement, sans l'élargir. /// /// Jusqu'à `+etm22`, une borne avait **deux commandeurs** : l'arbitre décidait et /// publiait, puis `adjustEvChargers()` re-décidait derrière lui et commandait autre