diff --git a/docs/DESIGN_10.md b/docs/DESIGN_10.md index b9efb5d..c740e2f 100644 --- a/docs/DESIGN_10.md +++ b/docs/DESIGN_10.md @@ -444,6 +444,17 @@ Cette étape les branche **en lecture seule** : `sessionWh` alimenté, `progress quand le régime est `unmeasurable` — jamais à zéro, sans quoi une dégradation se lirait comme un avancement nul, ce que `LM-1009 C1` interdit nommément. +> **⚠️ `sessionEnergy` porte un OFFSET — mesuré le 2026-08-31.** Sur la V2C, l'écart entre +> `sessionEnergy` et `chargeEnergy` est constant au dix-millième (7,2044), parce que +> l'accumulateur du plugin porte les incréments d'avant la dernière remise à zéro. **La valeur +> absolue n'est donc pas l'énergie de la session courante.** `deliveredWh` doit être une +> **différence depuis une base capturée**, jamais une lecture directe — sans quoi toute +> obligation éco se croirait tenue de 7,2 kWh dès le premier cycle. Un nombre crédible et faux, +> que rien ne signalerait. Détail et mesures : `RELEVE_APP_20260831.md`. +> +> Corollaire : **la V2C n'est plus un cas `unmeasurable`.** `LM-1009 C1` reste vraie pour les +> bornes sans l'état, mais le porteur d'exemple change — R6 devra citer une autre classe. + > **C'est ici que R5 devient vraie, et R6 devient TESTABLE avant d'être nécessaire.** Le régime > se publie sans qu'aucune décision ne s'y appuie : l'app peut afficher « non mesurable » sur la > V2C dès cette étape, et la garde de R6 — `ECO_FLOOR_MET` interdit sous `unmeasurable` — aura diff --git a/docs/RELEVE_APP_20260831.md b/docs/RELEVE_APP_20260831.md new file mode 100644 index 0000000..eeff77a --- /dev/null +++ b/docs/RELEVE_APP_20260831.md @@ -0,0 +1,80 @@ +# RELEVÉ — trois constats de l'agent app, à traiter APRÈS le §10 + +*(Remontés le 2026-08-31, mesurés sur `.75`. Rangés ici pour ne pas se perdre : ils ne passent +pas devant le §10, et aucun ne se déduit du code — les trois viennent de la machine.)* + +--- + +## 1. Une commande perdue pendant une indisponibilité n'est JAMAIS réappliquée + +**Mesuré.** La box journalise `Restoring manual values of false 6 A` trois secondes après une +coupure Modbus. La borne, elle, reste à `power: true`, **7 A** — ce que l'arbitre lui avait +commandé au cycle précédent. Le lien revient : **rien ne réapplique**. En mode manuel personne ne +commande, donc l'écart est **permanent**. + +**Ce que ça coûte, chiffré :** 4,4 kW réellement tirés, `draw.committedW` à **0**, et un budget de +surplus de **2 349 W** qui ignore ces watts. + +Une coupure d'**une seconde** suffit donc à laisser une borne à un courant que plus personne n'a +choisi, et le budget est faux tant que ça dure. Sur 18 kVA c'est invisible ; **sur 6 kVA c'est le +disjoncteur**. + +**La question posée par l'app** était étroite — « une entrée en mode manuel devrait-elle +réappliquer ses valeurs à la reconnexion du Thing ? ». Elle est **élargie** : toute commande +perdue pendant une indisponibilité devrait être **réappliquée au retour, ou la divergence +signalée**. + +**Et c'est calculable dès aujourd'hui.** On sait ce qu'on a commandé, la borne dit ce qu'elle +fait, et `measurement.source` (`LM-1105` / `LM-1106`) dit désormais **ce que vaut la +comparaison** — une divergence entre commandé et `device` n'a pas le même statut qu'une +divergence contre `none`. + +> **Même famille que toute la semaine** : le lien revient, l'état a l'air sain, `available` est +> vrai, et personne ne sait que la commande n'a jamais atterri. C'est `ECS-111` et `ECS-415` sous +> une troisième forme — après la télémétrie et après la valeur de retour, voici **l'exécution**. + +--- + +## 2. `chargingState` reste `ChargingStateIdle` pendant une charge RÉELLE + +**Mesuré.** Lien vivant, borne publiant 4 372 → 4 397 W, courants qui bougent — et le champ reste +à `Idle`. + +**Ce n'est donc PAS de la péremption**, et c'est ce qui rend le constat utile : personne ne +maintient le champ quand l'arbitre ne commande plus la borne. + +**Un champ que plus personne ne maintient devrait être ABSENT, pas figé.** Figé, un écran affiche +« à l'arrêt » pendant une charge. C'est l'invariant 12 d'`AGENTS.md` — un champ absent vaut mieux +qu'un champ nul ou forgé — appliqué à un champ dont le *mainteneur* a disparu, et non la valeur. + +--- + +## 3. `ChargingModeNormal` n'est pas un boost, et son nom le laisse croire + +Il **restaure des « valeurs manuelles mémorisées »**, qui peuvent être `power: false`, `6 A`. Y +passer peut donc **arrêter une charge** alors que l'utilisateur croit demander la pleine +puissance. Le nom du mode et son effet ne coïncident pas. + +**À documenter au minimum.** À corriger si l'on juge que le nom peut bouger — c'est une frontière +amont, et un renommage se paie côté clients. + +--- + +## Et un point OUVERT qui se CLÔT : la V2C publie `sessionEnergy` + +**Mesuré**, deux échantillons à deux heures d'écart sur charge réelle : l'écart entre +`sessionEnergy` et `chargeEnergy` est **constant au dix-millième** — 7,2044 dans les deux cas. +C'est l'accumulateur du plugin V2C qui porte les incréments d'**avant la dernière remise à +zéro**, et **ça ne se déduisait pas du code**. + +**Conséquences, et elles touchent l'étape C du §10 :** + +1. **`LM-1009 C1` reste vraie** pour les bornes qui n'ont pas l'état — mais **la V2C n'en fait + plus partie**. Le cas « réel dès aujourd'hui » qui motivait R6 change de porteur. +2. **`sessionEnergy` est l'accumulateur à lire** pour alimenter `LoadContextTelemetry::sessionWh`. +3. **⚠️ Il porte un OFFSET.** Sa valeur absolue n'est pas l'énergie de la session courante. + `deliveredWh` doit donc être une **différence depuis une base capturée**, jamais une lecture + directe — sinon toute obligation éco se croirait déjà tenue de 7,2 kWh au premier cycle. C'est + le genre d'erreur qui produit un nombre crédible et faux, et que rien ne signalerait. + +L'app a corrigé sa maquette et son modèle en conséquence.