Une dérogation n'est pas un mode. Un mode se choisit une fois et vaut jusqu'au prochain choix ; une dérogation répond à un besoin ponctuel et s'éteint avec lui. Un boost encore actif après la fin est un mode qu'on a oublié de quitter. Elle se superpose au mode courant, sert la charge à son MAXIMUM en tête de passe éco — devant toute autre charge, quel que soit son rang — financée au réseau. Le rang classe ce que l'installateur décide (LM-1002) ; une dérogation est une action ponctuelle de l'utilisateur, et passe par une porte que LM-1002 ne ferme pas. Mais elle ne surplombe PAS la sûreté : DRAW_CAP la borne et le délestage la reprend. Servie la première dans ce qui RESTE, jamais au-delà — sans quoi on rouvrirait le second chemin d'achat refusé au minimum réseau (LM-1500-e). LA DURÉE EST OBLIGATOIRE ET BORNÉE, refusée à la pose hors de [60 s, 6 h]. C'est la règle du lot : une dérogation qui ne sait pas dire quand elle finit ne devrait pas exister (LM-1500). Elle reste armée même avec une cible, parce qu'une cible repose sur un instrument et qu'un instrument peut se taire — à la perte du lien, la V2C FIGE sessionEnergy au lieu de l'effacer (LM-1012-d) : une dérogation « 20 kWh puis stop » dont le compteur reste à 12 kWh n'atteint jamais sa cible. Trois décisions prises en écrivant, absentes du design : - Les fins se lisent dans le CONTEXTE DU CYCLE, pas dans les adaptateurs. evaluerDerogations() tourne entre buildContext() et getPlan(), sur ce que le scheduler va lire. Une seconde lecture donnerait une seconde vérité, et une dérogation pourrait finir sur une mesure que le plan ne voit pas. C'est aussi ce qui rend le débranchement gratuit : la borne est déjà filtrée du contexte, son absence EST le signal. - measurementLost ne mord QUE s'il y a une cible (LM-1500-b). Sans cible, rien ne dépend de la mesure : couper là punirait la charge du silence d'un instrument dont elle n'a pas besoin. Et le critère est telemetry.sessionMeasurable, jamais progress.regime : `progress` est absent d'une charge sans obligation en énergie, c'est-à-dire exactement le cas du boost — le critère aurait été inerte. - La base de la cible se capture au PREMIER CYCLE, pas à la pose : au moment où l'utilisateur appuie, aucun cycle n'a construit de contexte. Prendre 0 compterait depuis le début de la session — une dérogation « 5 kWh » posée sur une session déjà à 6 kWh serait atteinte avant d'avoir commencé (LM-1012-b). Le mode précédent se MÉMORISE, il ne se déduit pas (LM-1500-d). Revenir à un défaut détruirait un réglage en silence, et sur une borne la sortirait de l'arbitrage : une dérogation qui laisse l'installation moins pilotée qu'avant elle. Une borne en manuel est basculée en éco pour la durée — seul cas où le mode est écrit, et modeForce retient qu'on l'a fait. La passe confort est SAUTÉE sous dérogation : la fusion des niveaux est une addition, et la dérogation demande déjà le maximum. Conséquence comptable à connaître : elle est comptée entièrement en achat alors que le surplus présent la finance en partie — gridW la surestime au soleil. Approximation héritée du §10, non aggravée, et la raison pour laquelle la première vérification au banc se fait le matin. La fin est un ÉVÉNEMENT et dit sa CAUSE (LM-1500-f), publiée dans derogationEnded[] au NIVEAU DE LA TRAME : au débranchement la charge n'est déjà plus dans loads[], et l'événement disparaîtrait quand il explique le plus de choses. Cinq causes, cinq gestes. Le mode dégradé la porte aussi — c'est là que measurementLost se produit. telemetrySignificantlyDiffers ne la filtre jamais : un événement ne vit qu'un cycle. Éprouvé par testDerogationTakesTheHeadButNotTheSafety, dont la tête de passe est prouvée par la RARETÉ : 7 500 W d'autorisation pour 8 000 demandés, la borne de rang 9 servie ENTIÈREMENT et l'ECS de rang 1 réduite aux 500 restants — l'inverse exact de l'ordre des rangs. Contre-épreuve : la même borne sans dérogation reçoit 0 W. Et à autorisation nulle, AUCUN niveau — pas un niveau à zéro. simulation 68/68, loadmodel 26/26, charging 48/48, spotmarket 32/32, ci-quality 4/4, build 0 erreur 0 warning. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015F7G5VeaPVSMeVNjiGj36p
16 KiB
Dérogations — le boost n'est pas un mode
Ouvert le 2026-09-07. Design avant code, rien d'implémenté. Cadrage donné par Patrick, et il vaut au-delà du VE.
1. La distinction qui structure tout le lot
| mode | dérogation | |
|---|---|---|
| se choisit | une fois | pour un besoin ponctuel |
| vaut | jusqu'au prochain choix | jusqu'à ce que le besoin cesse |
| se termine | jamais tout seul | toujours tout seul |
Un boost encore actif après la fin est un mode qu'on a oublié de quitter. Un boost qui rend la main est une dérogation terminée.
C'est le même geste que forcer un chauffe-eau parce qu'on veut plus d'eau chaude : on ne reconfigure pas l'installation, on demande quelque chose maintenant.
Conséquence de conception : ce n'est ni un ChargingMode de plus, ni une demande dans
needs. Une dérogation se superpose au mode courant et s'efface. Elle n'a ni cible d'énergie,
ni échéance, ni cadence — les trois supposent un besoin qui dure.
2. Ce qui la borne : la sûreté, et rien d'autre
Les trois comportements se distinguent par ce qui les borne :
| borné par | |
|---|---|
| PV | le surplus |
| Min+PV | un plancher + le surplus |
| Boost | la sûreté seule — DRAW_CAP et le délestage |
Et c'est cette dernière ligne qui le rend sûr : les plafonds deviennent la seule limite, donc
une limite obligatoire. Un boost sur un branchement 3×30 A sans phasePowerLimit ferait
disjoncter — ce n'est plus une hypothèse depuis le 2026-09-07, où l'on a découvert des semaines
de fonctionnement à 0.
Le boost prend la tête de la passe éco ; il ne surplombe pas la sûreté. Si DRAW_CAP mord, il
est servi le premier dans ce qui reste, pas au-delà. Sans cette précision on retomberait sur
le second chemin d'achat refusé pour le minimum (DESIGN_MIN_RESEAU.md §3).
Le rang
Le boost passe devant, y compris devant une charge mieux rangée. LM-1002 dit que le rang est
ce que l'installateur décide ; elle ne dit rien d'une action ponctuelle de l'utilisateur.
Prendre la tête ne contredit donc pas la règle : cela passe par une porte qu'elle ne ferme pas.
Un boost servi après une autre charge contredirait « tout de suite », qui est sa définition.
3. Précaution 1 — le mode précédent se MÉMORISE, il ne se déduit pas
À la fin de la dérogation, la charge revient au mode qu'elle avait, relu depuis ce qui a été stocké au déclenchement. Min+PV redevient Min+PV, PV redevient PV.
Revenir à un défaut serait un défaut. Retomber sur Normal sortirait la borne de l'arbitrage
— l'inverse exact du but, et une dérogation qui laisse l'installation moins pilotée qu'avant.
C'est la même famille que le repli d'échéance qui réécrivait EcoWithMinCurrent en Eco :
un état détruit en silence par un mécanisme qui croyait bien faire.
4. Précaution 2 — « terminé » se définit PAR MÉCANISME
Le moteur ne peut pas deviner ce qui met fin à une dérogation : cela dépend de ce qu'on déroge.
| charge | fins possibles |
|---|---|
| borne VE | débranchement, cible en ÉNERGIE (sessionEnergy), durée |
| chauffe-eau | température atteinte (si sonde), durée |
| batterie | cible en énergie, durée |
La cible se dit en ÉNERGIE, jamais en SOC — et ce n'est pas un pis-aller
Il n'y a pas de communication véhicule, et il n'y en aura pas : aucun plugin constructeur n'est prévu, et une intégration cloud Skoda/VW serait contraire au 100 % local, fragile, et rafraîchie à la minute au mieux.
Mais surtout, l'énergie est la bonne grandeur, indépendamment de cette contrainte :
| ce qu'il faut savoir pour l'affirmer | |
|---|---|
| « 20 kWh puis tu t'arrêtes » | sessionEnergy, que le plugin V2C construit depuis le 2026-08-29 |
| « jusqu'à 80 % » | la capacité, l'état de santé et le rendement de charge |
C'est exactement ce que LM-1009 a établi pour l'obligation éco : on mesure de l'énergie parce
que c'est ce qu'on sait mesurer. Un SoC demanderait trois inconnues pour dire la même chose en
moins sûr.
La fin par SOC ne figure donc pas dans ce design, et son absence n'est pas un manque à combler plus tard : c'est une décision. L'écrire comme un manque aurait laissé quelqu'un l'ouvrir comme un lot.
Et sessionEnergy a un silence connu, qui justifie le filet
LM-1012-d, mesuré sur la V2C le 2026-08-30 : à la perte du lien, currentPower tombe à 0
et les booléens à false, mais sessionEnergy reste à sa dernière valeur.
Une dérogation « 20 kWh puis stop » dont le compteur se fige à 12 kWh n'atteindra jamais sa cible. Elle achèterait au maximum jusqu'à ce que quelqu'un s'en aperçoive — sauf que la durée est là. Ce n'est pas un cas d'école : c'est le comportement documenté de la borne installée.
Chaque mécanisme déclare ce qui termine sa dérogation. C'est un ajout à ILoadAdapter, au
même titre que commandSettleS() : une propriété du mécanisme, pas une constante du moteur.
5. LA RÈGLE — une dérogation qui ne sait pas dire quand elle finit ne devrait pas exister
C'est la règle centrale du lot, et elle se teste.
Sur un chauffe-eau sans sonde, « jusqu'à ce que ce soit chaud » n'est pas mesurable. La fin doit alors être une durée, explicitement — et l'app doit le dire à la saisie, au moment où l'utilisateur croit demander une température.
C'est exactement le raisonnement de progressMeasurable : une capacité absente se dit à la
saisie, parce que l'écran de résultat parle trop tard. Une dérogation dont la fin n'est pas
observable est une dérogation qui ne finira pas.
La question qui manquait : que fait-elle ENTRE le figement et l'échéance ?
La durée arrêtera la dérogation — au bout. Entre-temps, elle achète au maximum sur une mesure morte. Sur un lien qui perd 40 % de ses trames, « au bout » peut vouloir dire des heures.
Position retenue : une dérogation dont la cible cesse d'être MESURABLE cesse. Elle n'attend pas sa durée.
C'est R6 transposée. Là-bas : servie oui, tenue jamais — on ne conclut pas « tenue » sur un
avancement qu'on ne mesure pas. Ici : active oui, vérifiable non, donc on s'arrête. Une
dérogation est une commande qui achète ; la maintenir sans pouvoir dire où elle en est, c'est
acheter à l'aveugle.
Et l'instrument existe déjà : measurement.source et le régime de LM-1009. Quand le régime
d'une charge en dérogation cesse d'être mesuré, la condition de fin est perdue — et cela se sait
au cycle même, pas au bout de la durée.
QUATRE fins, et le motif dit laquelle a mordu
| fin | ce qu'elle dit à l'utilisateur | geste |
|---|---|---|
targetReached |
la cible en énergie est atteinte | aucun — c'est le succès |
unplugged |
le véhicule est parti | aucun |
measurementLost |
on ne sait plus où en est la charge | regarder le lien / le compteur |
durationElapsed |
le temps imparti est écoulé | relancer si besoin |
Les quatre appellent des gestes différents, et une dérogation qui se terminerait sans dire laquelle a mordu laisserait l'utilisateur relancer un boost que le lien empêchera encore.
L'asymétrie, et la règle qu'elle donne
Le débranchement peut clignoter sur cette borne. Le plugin V2C a déjà la parade —
k_absencesPourClore polls consécutifs avant de clore une session — et elle porte une propriété
qu'il faut reprendre : seuls les polls RÉUSSIS font avancer le compteur. Une coupure réseau
le gèle, elle ne le fait pas progresser. Un lien mort ne peut donc pas fabriquer un faux
débranchement.
Cette confirmation vaut pour la dérogation aussi. Mais les deux erreurs ne coûtent pas la même chose :
Terminer à tort rend la main : la charge repasse à son mode mémorisé, et l'utilisateur relance s'il veut. Continuer à tort achète au maximum sur une mesure morte.
D'où la règle : dans le doute, une dérogation s'arrête. Les seuils de confirmation sont donc asymétriques par conception — prompts à conclure la fin, lents à conclure la poursuite. C'est l'inverse du réglage qu'on choisirait pour un mode, et c'est parce qu'une dérogation achète.
Corollaire — la DURÉE est un filet, pas une option
Toute dérogation porte une durée maximale, même quand elle a une meilleure condition de fin.
Raison : la condition de fin repose sur un instrument, et un instrument peut se taire. Une cible de charge qui ne remonte plus, une sonde qui tombe, un compteur qui se fige — et la dérogation ne se termine jamais, en tirant le maximum. C'est « l'instrument dont le silence ressemble à un résultat », appliqué à une commande qui achète.
Donc : jamais « jusqu'à annulation ». Quatre fins, et la durée est toujours l'une d'elles.
La durée est désormais le filet du filet : la perte de mesure attrape le cas où l'instrument se tait ET se signale ; la durée attrape le cas où même le régime ne se signale pas. Deux filets parce que le premier repose lui-même sur un instrument.
Cela rejoint ce que la maquette batterie demande pour son propre boost.
6. Ce que ça publie
Une dérogation active se voit, et sa fin se lit :
- un motif dédié, qui dit que la charge est en dérogation et ce qui y mettra fin ;
- l'instant de fin prévu, pour que l'écran affiche un compte à rebours plutôt qu'un état sans horizon ;
- l'instant est absolu, ancré à la capture (
toUTC()), jamais une heure murale — cf. la dérive mesurée le 2026-09-06.
Et la fin est un ÉVÉNEMENT, publié le cycle où elle survient. Sans lui, l'écran ne peut pas distinguer « la dérogation s'est terminée normalement » de « quelqu'un l'a annulée ».
7. Étapes, à comportement constant d'abord
A — le type et sa publication, sans qu'aucune dérogation ne puisse être posée. Le champ
existe, il ne décide de rien.
B — la déclaration des fins par mécanisme (ILoadAdapter), avec la durée obligatoire.
C — la pose, la mémorisation du mode précédent, et le retour automatique.
D — le service en tête de passe éco, borné par DRAW_CAP et le délestage.
Le pas D est le seul qui change ce qui est commandé, et il arrive quand tout le reste est déjà vérifiable.
8. Ce que le banc ne prouvera pas
Les trois fins du VE sont exerçables sans véhicule communicant : débranchement, cible en
énergie via sessionEnergy, et durée. C'est ce qui rend ce lot éprouvable sur le banc réel, là
où une fin par SoC ne l'aurait jamais été.
Reste hors de portée : la fin par température, qui demande la sonde du §12 — elle n'existe
qu'en mock, et c'est le sixième manque de INVENTAIRE_MOCK.md sous un autre angle.
La durée, elle, s'éprouve partout — une raison de plus d'en faire le filet plutôt qu'un recours.
9. Implémenté — +etm59, 2026-09-08
Le design ci-dessus est posé d'un bloc et non par les quatre pas du §7 : la fenêtre de peu de production du matin était le bon moment pour éprouver un mécanisme qui achète, et découper l'implémentation aurait fait passer cette fenêtre.
Où vit quoi
| fichier | |
|---|---|
| le type, les quatre fins, les seuils | etm/types/derogation.h |
| pose, levée, évaluation des fins, restitution du mode | etm/energyarbitrator.cpp |
| service en tête de passe éco | etm/scheduler/rulebasedscheduler.cpp |
SetLoadDerogation / ClearLoadDerogation, derogationEnded |
nymeaenergyjsonhandler.cpp |
Trois décisions prises en écrivant, qui ne sont pas dans le design
a. Les fins se lisent dans le CONTEXTE DU CYCLE, pas dans les adaptateurs.
evaluerDerogations() tourne entre buildContext() et getPlan(), sur les LoadContext
que le scheduler va lire. Une seconde lecture donnerait une seconde vérité, et une dérogation
pourrait se terminer sur une mesure que le plan ne voit pas. C'est aussi ce qui rend le
débranchement gratuit : une borne débranchée est déjà filtrée du contexte par
evParticipatesInWaterfall(), son absence est le signal.
b. measurementLost ne mord QUE s'il y a une cible.
Le design disait « une dérogation dont la cible cesse d'être mesurable doit cesser ». Sans cible,
rien ne dépend de la mesure : le filet de durée suffit, et couper là punirait la charge du
silence d'un instrument dont elle n'a pas besoin. La garde porte donc sur targetWh > 0.
C'est aussi pourquoi le critère est telemetry.sessionMeasurable et non progress.regime :
progress est absent sur une charge sans obligation en énergie — c'est-à-dire exactement le cas
du boost, où le critère aurait été inerte.
c. La base de la cible se capture au PREMIER CYCLE, pas à la pose.
Au moment où l'utilisateur appuie, aucun cycle n'a construit de contexte et le cumul de session
n'est lisible nulle part. Prendre 0 compterait depuis le début de la session de charge :
une dérogation « 5 kWh » posée sur une session déjà à 6 kWh serait atteinte avant d'avoir
commencé. Mécanisme de LM-1012-b, même raison — une base se prend sur ce qu'on mesure.
Le seul endroit où le mode est ÉCRIT
Une borne en Normal (manuel) est hors waterfall : le moteur ne la commande pas, donc une
dérogation n'y produirait rien du tout. La pose la bascule alors en éco pour la durée, et
modeForce retient qu'elle l'a fait. C'est le seul cas — et c'est lui qui donne sa raison d'être
à previousMode : sans mémorisation, on rendrait une borne manuelle en éco, c'est-à-dire qu'on
lui prendrait un réglage en silence.
La passe confort est SAUTÉE sous dérogation
La fusion des niveaux est une addition (la.powerW += eco.targetW). La dérogation demande
déjà le maximum de la charge : y ajouter du surplus dépasserait son plafond matériel. Le surplus
non dépensé reste au budget et passe aux charges suivantes.
Conséquence comptable à connaître : la dérogation est comptée entièrement en achat alors
que le surplus présent la finance en partie, physiquement — gridW la surestime dès qu'il y a du
soleil. Ce n'est pas une approximation neuve : le §10 la porte déjà pour le plancher éco, et elle
sera reprise avec lui. C'est aussi pourquoi la première vérification au banc se fait le matin.
Ce que le test prouve, et par quelle contre-épreuve
Simulation::testDerogationTakesTheHeadButNotTheSafety — deux charges, la borne au rang 9
(la queue) et l'ECS au rang 1, sans aucun surplus :
- contre-épreuve — la même borne sans dérogation reçoit
0 W, l'ECS ses 1 000 W de cadence. Sans elle, on ne saurait pas d'où viennent les 7 000 W du cas suivant ; - avec dérogation : 7 000 W,
funding = Grid, motifDEROGATION_ACTIVE,endsAtpublié, et pas detargetWhpuisqu'il n'y en a pas ; - la tête de passe prouvée par la rareté — 7 500 W d'autorisation pour 8 000 demandés : la borne de rang 9 est servie entièrement, l'ECS de rang 1 prend les 500 restants. L'inverse exact de l'ordre des rangs, donc la preuve que c'est bien la tête de passe qui agit ;
- 1 000 W d'autorisation : servie à 1 000,
boundBy: drawCap— elle ne surplombe pas la sûreté ; - autorisation nulle : aucun niveau, pas un niveau à zéro.
Ce qui reste ouvert
- La fin par cible n'est pas éprouvée au banc : elle demande une session assez longue pour
franchir un seuil, et la V2C a un
sessionEnergyqui se fige à la perte du lien (LM-1012-d) — c'est précisément ce que le filet de durée couvre. ClearLoadDerogationpublie la causecancelled, qui n'est pas une des quatre fins par mécanisme : c'est une fin par décision, et la distinguer est le but.