docs(spec §10): LM-1009 — une échéance ne vaut que ce que son avancement se mesure
Question remontée par l'agent app. Note de conception, aucun code : elle conditionne la forme du §10, elle ne la code pas. Le constat de l'app est exact — vérifié sur les cinq classes evcharger du banc, aucune ne déclare batteryLevel ni ne porte d'interface véhicule. Mais le code dit quelque chose de plus troublant que « il manque une mesure » : targetPercentage est comparé à carBatteryLevel, lu sur le Thing voiture. Et ce batteryLevel, dans le seul cas où le mécanisme s'active — une voiture manuelle, dont l'état est INSCRIPTIBLE — est écrit par le moteur lui-même : updateManualSoCsWithMeter() intègre la puissance de la borne, applique un facteur de pertes, divise par une capacité déclarée, et réinjecte le résultat dans l'état du Thing. Le moteur compare donc sa cible à sa propre estimation, bâtie sur trois grandeurs dont aucune n'est mesurée. La boucle se referme sur elle-même. Et l'estimation sort par la frontière RPC indistinguable d'une mesure — c'est l'app qui ment ensuite, de bonne foi. Pendant ce temps, la seule grandeur réellement mesurée n'est lue par personne : sessionEnergy est publiée par quatre des cinq classes, LoadContextTelemetry porte déjà son emplacement (sessionWh), et le champ n'est rempli nulle part. Réponse à la première question : OUI, et les trois pièces existent déjà — sessionEnergy pour mesurer, sessionWh pour la transporter, needs.minEnergyWhPerDay pour exprimer la cible. C'est la forme que le §10 doit retenir : une obligation en ÉNERGIE, vérifiable de bout en bout sans communication véhicule. Avec quatre réserves écrites, dont deux mordent tout de suite : sessionEnergy se remet à zéro au branchement (donc inutilisable telle quelle pour un minEnergyWhPerDay), et toutes les bornes ne la publient pas (donc la dégradation doit être VISIBLE). Réponse à la seconde : OUI, et elle tient MÊME avec la première — parce que targetPercentage ne disparaît pas. Deux objets, deux régimes de vérité : une obligation en énergie peut annoncer son atteinte, une cible en pourcentage publie une intention et un avancement inconnu ou estimé, jamais « tenue ». Même exigence que BELOW_MIN_POWER contre SURPLUS_INSUFFICIENT : deux causes, deux phrases, parce qu'elles n'appellent pas le même geste. Deux arbitrages sont laissés ouverts, dont un de lot : obligation par session (faisable en lisant un champ déjà publié) ou obligation quotidienne (qui exige l'historique du §11). LM-1205 (§12) renvoie désormais vers cette note : la machinerie d'échéance existe, son avancement ne se mesure pas. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017TVywtayEeoRJ2yWUW4iZj
This commit is contained in:
parent
d57a0184f3
commit
2385933b6a
@ -193,6 +193,11 @@ renuméroter les autres, ou assumer l'égalité et laisser l'app poser les vrais
|
||||
en service. Sans conséquence sur la reproductibilité (le tri est total), donc à décider, pas à
|
||||
réparer dans l'urgence.
|
||||
|
||||
Le **§10** attend une session propre. Deux notes de conception l'y attendent, à lire avant de
|
||||
coder : **LM-1006-1** (le délestage est un plafond de soutirage à trois sources) et **LM-1009**
|
||||
(une échéance ne vaut que ce que son avancement se mesure — `sessionEnergy` est publiée et lue
|
||||
par personne, tandis que le pourcentage comparé à la cible est estimé par le moteur lui-même).
|
||||
|
||||
Ensuite : **passe contrats** (OPTIMIZER_PROTOCOL + README).
|
||||
|
||||
> Le **trou de `pluggedIn`** trouvé au même passage est CORRIGÉ — une borne sans véhicule
|
||||
|
||||
@ -671,6 +671,106 @@ Le **dosage en Wh** viendra avec le modèle thermique, que l'historique du §11
|
||||
précisément d'apprendre. **Une raison de plus de faire le §11 en premier** : il ne prépare pas
|
||||
seulement les seuils, il prépare la seule chose qui rendra le dosage possible.
|
||||
|
||||
**LM-1009 — Une échéance ne vaut que ce que son avancement se mesure.** *(Question remontée par
|
||||
l'agent app, 2026-08-27. Note de conception : ne déclenche aucun travail, mais elle CONDITIONNE
|
||||
la forme du §10.)*
|
||||
|
||||
**Le constat de l'app, et il est exact.** Sur `.75`, aucune classe `evcharger` ne déclare d'état
|
||||
de charge et aucune ne porte d'interface véhicule — vérifié sur les cinq classes présentes
|
||||
(`terraAcTcp`, `terraAcRtu`, `wallbox` ×2, `wallboxNoMeter`) : `batteryLevel` absent partout.
|
||||
Les bornes publient `pluggedIn`, `charging`, `maxChargingCurrent`, `phaseCount` et, pour quatre
|
||||
d'entre elles, `sessionEnergy`. Jamais un pourcentage.
|
||||
|
||||
**Ce que le code fait aujourd'hui, et c'est plus troublant que « il manque une mesure ».**
|
||||
`targetPercentage` est comparé à `carBatteryLevel`, lu sur le Thing **voiture**
|
||||
(`smartchargingmanager.cpp:628`). Et ce `batteryLevel`, quand la voiture est une voiture
|
||||
*manuelle* — le seul cas où le mécanisme s'active, il exige un `batteryLevel` **inscriptible** —
|
||||
est **écrit par le moteur lui-même** : `updateManualSoCsWithMeter()` intègre la puissance de la
|
||||
BORNE, applique un facteur de pertes, divise par une `capacity` déclarée, et **réinjecte le
|
||||
résultat dans l'état du Thing** par une Action.
|
||||
|
||||
Autrement dit : le moteur estime un pourcentage à partir de trois grandeurs dont **aucune n'est
|
||||
mesurée** — le SoC de départ saisi par l'utilisateur, la capacité déclarée, un facteur de pertes
|
||||
constant — puis compare la cible à sa propre estimation. **La boucle se referme sur elle-même**,
|
||||
et rien, à aucun moment, ne lit l'état réel d'un véhicule.
|
||||
|
||||
> Le piège est là, et il touche l'app en premier : l'estimation est écrite dans `batteryLevel`,
|
||||
> donc elle sort par la frontière RPC **indistinguable d'une mesure**. Un écran qui affiche
|
||||
> « 62 % » affiche une intégration, pas une lecture. Si le §10 conserve ce chemin, ce champ doit
|
||||
> être marqué comme ESTIMÉ là où il est publié — sans quoi l'app ment de bonne foi.
|
||||
|
||||
**Et pendant ce temps, la grandeur qui EST mesurée n'est lue par personne.** `sessionEnergy` —
|
||||
l'énergie livrée depuis le branchement, comptée par la borne — est publiée par quatre des cinq
|
||||
classes du banc. Le contrat porte déjà son emplacement : `LoadContextTelemetry::sessionWh`
|
||||
(`surpluscontext.h:77`). **Il n'est rempli nulle part**, et `EvCharger` n'expose même pas
|
||||
d'accesseur. Champ déclaré, jamais alimenté.
|
||||
|
||||
### Réponse à la première question : OUI, et le modèle a déjà la bonne forme
|
||||
|
||||
Un avancement utilisable se reconstruit sans communication véhicule, et les trois pièces
|
||||
existent déjà :
|
||||
|
||||
| Ce qu'il faut | Ce qui existe | État |
|
||||
|---|---|---|
|
||||
| une grandeur mesurée | `sessionEnergy` de la borne | publiée, **jamais lue** |
|
||||
| un emplacement au contrat | `LoadContextTelemetry::sessionWh` | déclaré, **jamais rempli** |
|
||||
| une cible dans la même unité | `needs { minEnergyWhPerDay }` (LM-1001) | déclaré, **jamais lu par le scheduler** |
|
||||
|
||||
**C'est la forme que le §10 doit retenir** : une obligation exprimée en **énergie**, mesurée par
|
||||
l'énergie livrée. Elle est vérifiable de bout en bout, et elle est déjà commune à toutes les
|
||||
charges — un ballon et une borne portent le même `needs`, ce qui est exactement ce que LM-1001
|
||||
annonçait.
|
||||
|
||||
**Quatre réserves, et chacune devient un défaut si on l'oublie :**
|
||||
|
||||
1. **Livré ≠ stocké.** `sessionEnergy` est comptée à la borne ; les pertes de charge font que le
|
||||
véhicule en reçoit moins. Ce n'est pas un défaut de la mesure — c'est le bon côté du compteur
|
||||
pour une **obligation d'énergie** : celui qu'on paie. Mais la promesse doit se formuler
|
||||
« livrer X kWh », jamais « mettre X kWh dans la batterie ».
|
||||
2. **`sessionEnergy` se remet à zéro au branchement.** Parfait pour une obligation *par session*,
|
||||
**inutilisable tel quel** pour un `minEnergyWhPerDay` : débrancher et rebrancher relance le
|
||||
compteur, et l'obligation du jour se croirait à zéro. L'accumulation entre sessions est un
|
||||
mécanisme distinct, et sa place naturelle est l'historique du §11.
|
||||
3. **Toutes les bornes ne la publient pas** — mesuré : `wallboxNoMeter` et l'une des deux classes
|
||||
`wallbox` ne l'ont pas. La dégradation doit être **VISIBLE** : une échéance qui cesse
|
||||
silencieusement d'être mesurable est pire qu'une échéance absente, parce qu'elle continue de
|
||||
s'afficher. Même exigence que `hasPowerMeter()`, qui est déjà testé avant d'estimer un SoC.
|
||||
4. **Elle ne répondra JAMAIS à « la voiture sera-t-elle pleine ? »** Aucune énergie livrée ne dit
|
||||
où en est une batterie dont on ne connaît ni l'état de départ ni la capacité réelle. Une cible
|
||||
en énergie est vérifiable ; une cible en pourcentage ne l'est pas. Ce sont **deux objets
|
||||
différents**, et le §10 ne doit pas les confondre sous le mot « échéance ».
|
||||
|
||||
### Réponse à la seconde question : OUI, et elle tient MÊME avec la première
|
||||
|
||||
Répondre « oui » à la première ne dispense pas d'écrire la seconde, parce que `targetPercentage`
|
||||
**ne disparaît pas** : c'est un champ amont, c'est ce que l'app affiche, et c'est ce que
|
||||
l'utilisateur saisit. Deux objets, donc deux régimes de vérité, et le motif publié doit les
|
||||
distinguer :
|
||||
|
||||
| Objet | Vérifiable ? | Ce que le motif a le droit de dire |
|
||||
|---|---|---|
|
||||
| obligation en **énergie** (`needs`) | **oui**, par `sessionEnergy` | « X kWh livrés sur les Y demandés » — et l'atteinte peut être annoncée |
|
||||
| cible en **pourcentage** (`targetPercentage`) | **non**, sans communication véhicule | l'intention déclarée, et **l'avancement comme inconnu ou estimé** — jamais comme tenu |
|
||||
|
||||
**Règle à tenir dès l'écriture** : *un motif n'affirme pas ce que le moteur ne mesure pas.* Une
|
||||
échéance qu'on ne peut pas vérifier ne doit jamais se présenter comme tenue, ni comme en voie de
|
||||
l'être. C'est la même exigence que celle qui sépare `BELOW_MIN_POWER` de `SURPLUS_INSUFFICIENT`
|
||||
(§12) et que LM-1006-1 pose pour la source d'un plafond : **deux causes, deux phrases**, parce
|
||||
qu'elles n'appellent pas le même geste. Ici le geste diffère radicalement — « attends, ça
|
||||
charge » contre « je ne peux pas savoir où tu en es ».
|
||||
|
||||
> **Ce que cette note NE tranche pas**, et qui demande une décision :
|
||||
> — l'app garde-t-elle un écran en pourcentage, alimenté par l'estimation clairement étiquetée,
|
||||
> ou bascule-t-elle en énergie livrée, qui est la seule chose que la box sait prouver ?
|
||||
> — `minEnergyWhPerDay` exige-t-il l'accumulation inter-sessions du §11, ou le §10 se limite-t-il
|
||||
> d'abord à une obligation **par session**, qui ne demande rien de plus que de lire un champ
|
||||
> déjà publié ?
|
||||
>
|
||||
> La seconde est le vrai arbitrage de lot : l'obligation par session est faisable avec
|
||||
> `sessionEnergy` seul, l'obligation quotidienne demande un historique.
|
||||
|
||||
---
|
||||
|
||||
**LM-1007 — Compatibilité descendante, et elle est gratuite.** Une charge qui ne déclare
|
||||
**aucun** besoin éco ne participe qu'à la passe 2 — soit exactement le comportement
|
||||
d'aujourd'hui. Le modèle à deux niveaux **dégrade proprement** vers le modèle actuel, ce qui
|
||||
@ -866,6 +966,11 @@ l'émettre :
|
||||
négociation de courant réelle, le délai d'établissement de la charge, et le comportement d'un
|
||||
véhicule qui refuse.
|
||||
|
||||
> ⚠️ **À lire avec LM-1009 (§10)** : la machinerie existe, mais son AVANCEMENT ne se mesure pas.
|
||||
> `targetPercentage` est comparé à un pourcentage que le moteur a lui-même estimé, et la seule
|
||||
> grandeur réellement mesurée — `sessionEnergy` — n'est lue nulle part. Une échéance en énergie
|
||||
> est vérifiable ; une échéance en pourcentage ne l'est pas.
|
||||
|
||||
**LM-1205 — La machinerie d'échéance existe déjà.** `needs { dailyDeadline, minEnergyWhPerDay }`
|
||||
est ce qui avait motivé le proxy — la crainte que le waterfall ne sache pas exprimer « cette
|
||||
voiture doit être chargée à 7 h ». Il sait : c'est le même champ que le plancher éco du §10
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user