docs: design du minimum garanti — option 2, avec le renommage

Design avant code, comme pour le délestage et le §10. Rien d'implémenté.

LE CONSTAT EST DOUBLE. Le mode n'a jamais eu de minimum garanti — mort par
réécriture chez l'amont, puis mort par exclusion depuis 3g-2 (DESIGN_3g §5). Et
son nom promet l'inverse : `MinCurrent` désigne L'EXIGENCE DE LA BORNE, les 6 A
sous lesquels elle refuse de charger, pas une garantie du mode. Trois modes
annoncés, deux implémentés, et celui qui manque est celui que l'utilisateur
croyait utiliser — le défaut n'est pas qu'une fonction manque, c'est qu'elle est
annoncée.

POURQUOI MAINTENANT, sans attendre le prix. Rachat à 0,01 € : charger 30 % au
réseau en journée revient à 0,073 €/kWh contre 0,180 € la nuit. Sur la majorité du
parc neuf, aucun arbitrage n'est nécessaire pour savoir que c'est le bon choix. Le
prix ne devient décisif que sur les anciens contrats à 0,1296 €, où l'écart tombe
à deux centimes et s'inverse sous 62 % de couverture solaire. L'option 3 reste
l'horizon ; elle choisira S'IL FAUT garantir, pas COMMENT.

CONDITION 1 — machinerie du §10 : passe éco, borné par DrawCap, funding Grid,
motif qui dit combien est acheté. Exclut explicitement EV_GRID_START, dont les
trois bornes dérivent du surplus et qui achète hors du registre d'autorisation.

CONDITION 2 — un champ de plus DANS needs, frère de minEnergyWhPerDay, pas une
structure parallèle : `guaranteedPowerW`. À ne pas confondre avec minPowerW
(LM-1201) : le premier borne une consigne SI on sert, le second en crée une
TOUJOURS. La passe éco rend max(cadenceObligation, guaranteedPowerW), avec quatre
invariants — la garantie ne se tient jamais (donc watts retombe sur elle quand
l'obligation est tenue, pas sur zéro) ; progress ne décrit que l'obligation et est
ABSENT sur une garantie seule ; le motif dit lequel des deux mord ; une garantie
non honorée se dit par DRAW_CAP qui la nomme.

CONDITION 3 — le fil ne peut pas être renommé sans casser les clients déployés.
Ce qui se corrige est la DESCRIPTION au contrat, d'où l'app tire ses libellés.
ChargingModeNormal n'est pas « recharge immédiate au maximum » : c'est MANUEL, la
borne sort de l'arbitrage et le moteur ne commande rien — seule la protection de
surcharge la touche encore, ce qui doit rester et doit être dit.

QUATRE ÉTAPES, à comportement constant d'abord : corriger le contrat (aucun code),
porter et publier le champ sans s'en servir, la passe éco le prend en compte, puis
le mode s'y branche.

ET CE QU'ON NE SAURA PAS PROUVER AU BANC : 2 kWc contre un plancher de 4 140 W,
donc une garantie toujours financée au réseau et jamais le cas mixte. Le
chauffe-eau l'exercera dès qu'il aura un compteur réel — quatrième nomination de
la même précondition.
This commit is contained in:
Patrick Schurig 2026-09-06 14:51:35 +02:00
parent fd516daea5
commit b5efcc6357

148
docs/DESIGN_MIN_GARANTI.md Normal file
View File

@ -0,0 +1,148 @@
# Minimum garanti — design
> Ouvert le 2026-09-06. **Option 2 retenue** : livrer le minimum garanti maintenant, avec la
> machinerie du §10 ; le renommage l'accompagne. L'arbitrage au prix (option 3) reste l'horizon,
> il demande une source tarifaire et une prévision.
## 1. Le constat, et il est double
**Le mode `ChargingModeEcoWithMinCurrent` n'a jamais eu de minimum garanti** — non pas parce que
le code manquait, mais parce qu'il n'a jamais été atteint. Établi dans `DESIGN_3g.md` §5 : mort
par réécriture chez l'amont, puis mort par exclusion depuis 3g-2.
**Et son nom promet l'inverse de ce qu'il fait.** `MinCurrent` désigne **l'exigence de la borne**
— les 6 A en dessous desquels elle refuse de charger — **pas une garantie du mode**. Un lecteur y
voit « le mode maintient un courant minimum » ; le code, lui, ne fait que nommer un seuil
matériel.
> **Trois modes annoncés, deux implémentés — et celui qui manque est celui que l'utilisateur
> croyait utiliser.** C'est la formulation à retenir : le défaut n'est pas qu'une fonction
> manque, c'est qu'elle est *annoncée*.
## 2. Pourquoi livrer le minimum maintenant, sans attendre le prix
Chiffrage retenu, tarif de rachat récent à **0,01 €/kWh** :
| | coût effectif |
|---|---|
| charger en journée, 30 % au réseau | **0,073 €/kWh** |
| charger la nuit, 100 % au réseau | **0,180 €/kWh** |
Sur ces installations — **la majorité du parc neuf** — le minimum garanti en journée est
simplement le bon choix, et **aucun arbitrage n'est nécessaire pour le savoir**. Le prix ne
devient décisif que sur les anciens contrats à **0,1296 €**, où l'écart tombe à deux centimes et
**s'inverse sous 62 % de couverture solaire**.
**Conclusion de conception** : l'option 3 (comparer deux scénarios au prix) est le bon horizon,
mais elle exige une source tarifaire et une prévision. L'option 2 livre l'utile aujourd'hui, et
elle ne ferme pas la porte : un arbitrage au prix choisira plus tard *s'il faut* garantir, pas
*comment* garantir.
## 3. Condition 1 — la machinerie du §10, pas un second chemin d'achat
Le minimum garanti **passe par la passe éco**, celle qui achète déjà :
- **borné par l'autorisation de soutirage** (`DrawCap` / `DrawMargin`, trois sources) — comme
tout achat ;
- **`funding: Grid`** quand le surplus ne couvre pas ;
- **le motif dit combien est acheté**, comme `ECO_FLOOR_GRID` le fait déjà.
**Ce que cela EXCLUT explicitement** : porter le minimum par `EV_GRID_START`. Ses trois bornes
dérivent toutes du surplus (`budgetW > 0`, seuil proportionnel, consommation de tout le budget) ;
un minimum permanent en est indépendant par définition, donc les lever revient à créer le second
mécanisme. Et `EV_GRID_START` achète **hors** du registre d'autorisation — deux mécanismes dont
un seul est borné, et c'est le second qui ferait disjoncter. Raisonnement complet dans
`DESIGN_3g.md` §5.4.
## 4. Condition 2 — une puissance permanente, portée sans dupliquer `needs`
### Le champ
Un champ de plus **dans `needs`**, frère de `minEnergyWhPerDay` — pas une structure parallèle :
```
needs : {
minEnergyWhPerDay : Uint, // énergie, avec échéance
dailyDeadline : String,
guaranteedPowerW : Uint // NOUVEAU — puissance, permanente
}
```
**À ne pas confondre avec `minPowerW`** (`LM-1201`), et la différence est de nature :
| | ce que ça dit |
|---|---|
| `minPowerW` | **SI** on sert cette charge, au moins ceci. Un plancher de **modulation**. |
| `needs.guaranteedPowerW` | On sert cette charge au moins ceci, **TOUJOURS** — surplus ou non. Une obligation de **présence**. |
Le premier borne une consigne ; le second en crée une. Les deux peuvent coexister sur la même
charge sans se contredire.
### La composition dans la passe éco
`computeEcoTarget()` rend aujourd'hui une cadence dérivée de l'obligation en énergie. Elle rendra
le **maximum des deux** :
```
watts = max( cadenceObligation , guaranteedPowerW )
```
Quatre invariants, et chacun ferme un silence :
1. **La garantie ne se « tient » JAMAIS.** `tenue` ne décrit que l'obligation en énergie ; une
puissance permanente n'a pas de cible et ne se complète pas. Donc quand l'obligation est
tenue, `watts` retombe sur la garantie — **pas sur zéro**. C'est exactement la différence de
nature, et c'est là qu'elle se voit dans le code.
2. **`progress` ne décrit que l'obligation.** Sur une charge à garantie seule, `progress` est
**absent** — jamais à zéro : un avancement nul se lirait « rien livré », alors qu'il n'y a rien
à avancer.
3. **Le motif dit LEQUEL DES DEUX MORD.** Sans ça, l'écran affiche un nombre qu'il ne peut pas
expliquer — et deux causes différentes produiraient le même chiffre. Le motif gagne
`guaranteedW` et une marque de ce qui borne.
4. **Une garantie non honorée se DIT.** Si l'autorisation de soutirage ne la permet pas, elle
n'est pas servie — et c'est `DRAW_CAP` qui le dit, en nommant la garantie. Le silence ici
ferait croire à une garantie tenue.
## 5. Condition 3 — nommer les trois modes pour ce qu'ils font
**Le fil ne peut pas être renommé** : `ChargingMode` est une énumération publiée, et changer ses
identifiants casserait tout client déployé. Ce qui se corrige, c'est **la description au
contrat** — c'est là que vit le mensonge, et c'est de là que l'app tire ses libellés.
| valeur | ce que le contrat dit AUJOURD'HUI | ce que le code FAIT |
|---|---|---|
| `ChargingModeNormal` | « Recharge immédiate au maximum disponible » | **MANUEL.** La borne sort de l'arbitrage — *« l'utilisateur pilote, l'arbitre s'abstient »*. Le moteur ne commande **rien**. Seule la protection de surcharge la touche encore. |
| `ChargingModeEco` | « Recharge sur surplus solaire uniquement » | exact |
| `ChargingModeEcoWithMinCurrent` | « Eco + courant minimum garanti (6 A) si pas de surplus » | **rien** — le chemin n'est pas atteint |
**`Normal` n'est pas un « boost ».** Un boost commanderait le maximum ; celui-ci ne commande pas.
La seule chose que le moteur y fasse encore est de délester en surcharge — ce qui doit rester, et
doit être **dit** : un mode « manuel » qui coupe quand même mérite de l'annoncer.
## 6. Les étapes, à comportement constant d'abord
**Étape A — corriger le contrat.** Les trois descriptions ci-dessus, plus la note sur `MinCurrent`
qui nomme l'exigence de la borne. **Aucun code.** Comportement constant par construction, et c'est
le pas qui rend la suite lisible.
**Étape B — porter `guaranteedPowerW` dans `LoadNeeds`, le persister, le publier**, sans que la
passe éco s'en serve. Aller-retour `Get`/`SetLoadConfig`, schéma déclaré une fois. Comportement
constant : le champ existe et ne décide de rien.
**Étape C — la passe éco le prend en compte.** Le seul pas qui change le comportement, et il
arrive quand tout le reste est déjà en place et vérifiable. Avec les quatre invariants du §4, le
motif enrichi, et le `DRAW_CAP` qui nomme la garantie.
**Étape D — le mode s'y branche.** `ChargingModeEcoWithMinCurrent` posé sur une borne écrit
`guaranteedPowerW = plancher de la borne`. C'est le pas qui rend la promesse du nom vraie — et il
vient en dernier, parce qu'il n'a de sens qu'une fois C livrée.
## 7. Ce qu'on ne saura pas prouver au banc, et il faut le dire
L'installation de `.75` produit **2 kWc** contre un plancher de borne triphasée à **4 140 W**. Une
garantie sur cette borne serait donc **toujours** financée au réseau, jamais au surplus — le banc
ne peut pas exercer le cas mixte, celui où la garantie complète un surplus partiel.
Il l'exercera en revanche très bien sur une charge en watts — le chauffe-eau — dès qu'elle aura un
compteur réel (`TEST_TERRAIN.md` §12). **C'est la même précondition que pour l'étape 4 de la
commande perdue**, et elle est maintenant nommée par un quatrième chemin.