Mesuré AVANT d'implanter, et la mesure a corrigé le diagnostic. Le constat disait « un budget de surplus qui ignore ces watts ». Ce n'est pas ce qui se passe : quand une charge consomme et OBÉIT, la comptabilité est juste — sa conso est recréditée puis réallouée à elle-même, et les suivantes ne reçoivent que le surplus réel. Export 500 W, A à 4 400 W obéissante, B reçoit 500 W. Corriger cela aurait été corriger un non-défaut. Le vrai défaut est plus grave, et l'hypothèse est écrite dans le code : « le budget est une mesure dont la conso de la charge a déjà été retranchée », c'est-à-dire « je peux rendre ces watts parce que je peux les reprendre ». C'est un prêt, et rien ne vérifie que l'emprunteur rembourse. Mesuré, le cas où la promesse est engagée : A commandée à 0 par son plancher, ses 4 400 W redistribués à B. Si A obéit, tout est juste. Si A n'obéit pas — commande perdue, mode manuel, véhicule qui limite — la maison tire 8 400 W pour 4 900 de production : 3 500 W importés que personne n'a décidés. Et l'identité surplus + recrédit − alloué == restant TIENT dans les deux cas. C'est ce qui distingue un prêt d'une erreur : la comptabilité est juste, c'est l'hypothèse sur laquelle elle repose qui n'est pas vérifiée — donc aucun contrôle de cohérence ne pouvait l'attraper. Le test épingle le comportement ACTUEL, correct SOUS HYPOTHÈSE, et dit à quelle condition il devient faux : le jour où le recrédit sera conditionné à l'obéissance constatée, c'est l'allocation de B qu'il faudra revoir, pas le test. Écrit pour qu'on ne le « corrige » pas par réflexe. Ce que le design proposait — retrancher la conso subie du budget — est SANS OBJET : elle en est déjà retranchée par le compteur. La note est conservée pour que personne ne la réimplante en la croyant oubliée. Suite : simulation 60/60, loadmodel 22/22, charging 48/48 identique à la référence, doxygen 0 avertissement. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015F7G5VeaPVSMeVNjiGj36p
8.6 KiB
DESIGN — la commande perdue, et l'écart que personne ne mesure
(Ouvert le 2026-09-01, sur le premier constat de RELEVE_APP_20260831.md. Le design avant le
code, comme pour le délestage et le §10.)
1. Le fait, tel qu'il a été mesuré
Coupure Modbus. La box journalise Restoring manual values of false 6 A trois secondes après.
La borne, elle, reste à power: true, 7 A — la consigne que l'arbitre lui avait donnée au
cycle d'avant. Le lien revient : rien ne réapplique. En mode manuel personne ne commande,
donc l'écart est permanent.
4,4 kW réellement tirés, draw.committedW à 0, un budget de surplus de 2 349 W qui ignore ces
watts.
2. Ce que le fait dit vraiment — et il est plus large que son titre
« Réappliquer la commande de l'arbitre » ne décrit pas le cas : en mode manuel, l'arbitre ne commande pas. Ce qui s'est passé est autre chose, et c'est pire :
- Le proxy amont a voulu restaurer des valeurs manuelles (
false 6 A) ; - l'écriture a échoué pendant l'indisponibilité ;
- personne ne l'a rejouée ;
- l'organe est resté sur une consigne dont plus aucun commandeur ne se réclame — ni l'arbitre, qui ne commande plus, ni le mode manuel, dont la consigne n'a pas atterri.
La troisième forme de la même famille. Après la télémétrie qui ne distinguait pas une issue de son contraire (
ECS-111) et la valeur de retour qui faisait passer un refus pour un succès (ECS-415), voici l'exécution : une écriture perdue, un état sain en apparence,availableà vrai, et personne pour savoir que la commande n'a jamais atterri.
3. Ce que le moteur a déjà — et ce qui manque
| Pièce | État |
|---|---|
| Dernière consigne écrite, charges en watts | existe — m_currentSetpointW, m_currentStage |
| Dernière consigne écrite, bornes VE | MANQUE — EvAdapter ne garde que m_lastActionAt |
| Ce que l'organe FAIT | existe — EvCharger::maxChargingCurrent(), états du Thing |
| Ce que l'organe MESURE, et ce que ça vaut | existe — deviceMeasurement(), measurement.source (LM-1105/LM-1106) |
| Détection de la FIN d'une indisponibilité | MANQUE — le seul chemin est clearFault(), actionné par l'opérateur |
| Comparaison commandé / constaté | MANQUE — nulle part |
Le constat porte sur les bornes, et c'est précisément là que la mémoire de la commande manque. Ce n'est pas un hasard : les adaptateurs de charges en watts gardent leur consigne parce qu'ils en ont besoin pour l'écrêtage de verrou ; la borne, elle, était pilotée par le proxy amont, qui n'a jamais eu à se relire.
4. LES DÉCISIONS
4.1 — Réappliquer, ou signaler ? Réappliquer automatiquement une consigne vieille de plusieurs minutes est un geste dangereux : le monde a changé pendant l'indisponibilité — le surplus, l'heure, l'échéance. Rejouer une décision périmée, c'est décider avec des données mortes, exactement ce que le mode dégradé L2 refuse de faire.
Recommandation : SIGNALER toujours, RÉAPPLIQUER jamais telle quelle. Au retour de disponibilité, le cycle normal reprend la main et décide sur des données fraîches — c'est déjà le bon comportement. Ce qui manque n'est pas le rejeu, c'est que l'écart soit dit tant qu'il dure. Et il dure indéfiniment quand personne ne reprend la main, ce qui est le cas du relevé.
4.2 — Que publie-t-on, et sous quelle forme ?
Un écart n'a de sens que rapporté à ce qu'on sait de l'organe. Proposition : un objet command
au niveau de la charge, publié seulement quand la comparaison est possible —
"command": { "expectedW": 1380, "observedW": 4400, "divergent": true, "since": "…" }
measurement.source conditionne ce que vaut cette comparaison, et doit donc la gouverner :
contre none, il n'y a rien à comparer et l'objet est absent — pas divergent: false, qui
affirmerait la concordance qu'on ne peut pas constater. Même règle que partout ailleurs ici.
4.3 — La COMPTABILITÉ : le diagnostic était à corriger, et le vrai défaut est plus grave.
MESURÉ le 2026-09-01, avant d'implanter. Le constat disait « un budget de surplus qui ignore ces watts ». Ce n'est pas ce qui se passe. Quand une charge consomme et obéit, la comptabilité est juste : sa conso est recréditée puis réallouée à elle-même, et les suivantes ne reçoivent que le surplus réel (mesuré : export 500 W, A à 4 400 W obéissante → B reçoit 500 W). Corriger cela aurait été corriger un non-défaut.
Le vrai défaut : le recrédit est un PRÊT consenti contre l'obéissance. Son hypothèse est écrite dans le code — « le budget est une mesure dont la conso de la charge a déjà été retranchée » — c'est-à-dire « je peux rendre ces watts parce que je peux les reprendre ». Rien ne vérifie que l'emprunteur rembourse.
Mesuré, le cas où la promesse est engagée : export 500 W, A consomme 4 400 W, plancher de A à 5 000 W. A est commandée à 0, et ses 4 400 W sont redistribués à B.
| si A obéit | si A n'obéit pas | |
|---|---|---|
| A | tombe à 0 | reste à 4 400 W |
| B | 4 000 W réels | 4 000 W fictifs |
| maison | 4 000 W pour 4 900 de production | 8 400 W pour 4 900 → 3 500 W importés |
Et l'identité de réconciliation TIENT dans les deux cas — surplus + recrédit − alloué == restant. C'est ce qui distingue un prêt d'une erreur : la comptabilité est juste, c'est
l'hypothèse sur laquelle elle repose qui n'est pas vérifiée. Aucun contrôle de cohérence ne
pouvait donc l'attraper.
Ce qui change maintenant : l'obéissance est CONSTATABLE. command.divergent (étape 2) dit
si la charge a suivi au cycle précédent. Le recrédit peut donc être conditionné — c'est la
proposition, et elle demande une décision parce qu'elle a son propre risque : l'anti-clignotement
existe pour une raison, et le suspendre trop vite ferait osciller une charge saine sur un écart
transitoire. since est précisément ce qui permet de distinguer les deux.
Ce que le §4.3 d'origine proposait — retrancher la conso subie du budget — est SANS OBJET : elle en est déjà retranchée, par le compteur. La note est conservée ici pour que personne ne la réimplante en la croyant oubliée.
4.3-bis — l'ancienne formulation, conservée pour mémoire.
draw.committedW à 0 pendant que 4,4 kW sont tirés n'est pas un défaut d'affichage : le budget
de surplus est calculé sans ces watts, donc le moteur alloue de l'énergie qui n'existe pas.
Une charge que l'arbitre ne commande plus mais qui consomme est de la consommation fixe, au
même titre que la base de la maison — c'est exactement le raisonnement d'ECS-410 pour une
charge gelée, appliqué à une cause nouvelle.
Recommandation : la retrancher du budget, sans l'allouer. Elle n'est pas servie, elle est subie. La compter comme allouée serait prétendre l'avoir décidée.
Sur 18 kVA c'est invisible ; sur 6 kVA c'est le disjoncteur. Et le délestage ne rattrape pas ce cas : il agit sur la marge MESURÉE, qui inclut bien ces 4,4 kW — donc il protège. Ce qui reste faux est le budget, qui promet un surplus déjà consommé.
4.4 — Faut-il un état « commande en vol perdue » distinct d'un défaut ?
ECS-410/ECS-414 couvrent l'écriture qui échoue trois fois → défaut. Ici l'écriture a échoué
pendant une indisponibilité, et la charge est ensuite redevenue disponible : le défaut se
lève, la divergence reste. À trancher : est-ce un faultCode de plus, ou un état orthogonal ?
5. ORDRE DE TRAVAIL — deux étapes à comportement constant d'abord
Étape 1 — la MÉMOIRE de la commande, sans rien en faire. EvAdapter retient sa dernière
consigne appliquée, comme les autres adaptateurs le font déjà. Publication seule, comportement
constant. Sans cette pièce, aucune des suivantes n'est calculable.
Étape 2 — l'ÉCART publié, sans décider dessus. L'objet command, gouverné par
measurement.source. C'est la moitié qui rend le défaut visible, et elle ne change aucune
décision — même stratégie qu'à l'étape C du §10, où publier le régime a précédé de décider avec.
Étape 3 — la COMPTABILITÉ corrigée. La conso subie sort du budget. C'est ici que le comportement change, et c'est la seule étape qui le fasse.
Étape 4 — la reprise en main, si elle est jugée nécessaire après avoir vu les traces des étapes 2 et 3. Délibérément en dernier : c'est la seule qui commande du matériel, et on saura alors combien de fois le cas se produit réellement.