Le repli ne ressuscite plus le defaut — la part voiture vaut dans les deux branches

Ecrit par la session voisine docker22-b2, verifie et commite ici : `git` echoue
chez elle sur ce depot (dubious ownership) et elle ne touche pas a
`safe.directory` sans consigne du proprietaire. Laisser ce travail non suivi
dans un arbre partage en NFS, c est l etat qui se fait ecraser au premier
`n8n-extract`.

Quand la garde du bilan tombait — statut degrade, mesure perimee, champ
illisible — la consigne revenait a `maison + plafond de borne`, y compris sans
voiture. Le defaut d origine survivait donc exactement la ou le systeme est
deja degrade. Mesure sur le chemin concerne (midi, fenetre HC autorisee, aucun
vehicule, bilan indisponible) : 5725 W autorises pour une maison a 900 W.

Deux changements, aucune variable ajoutee. La branche de repli du plafond
emploie `evcsSetpointShareW` au lieu du plafond brut ; et `gridSetpointLoadW`
n a plus qu une branche, la distinction n ayant de sens que tant que le repli
gardait l emprunt. L invariant du 01/09 et du 04/09 reste tenu PAR LA FORME :
le plafond vaut maison plus un terme positif ou nul.

⚠️ Le commentaire conserve la trace d un disjoint
`nightGridSessionCommitted || forceGridRequested` sur la part voiture, ecrit
puis MESURE puis retire : il ecrase le terme batterie du bilan, puisque
max(4825 emprunte, 1538 mesure) retient l emprunt. Cout mesure, trois attendus
figes d un coup — 2380 -> 5437, 2380 -> 5437, 4219 -> 6301. Il avait un sens
tant que la consigne n avait aucun terme batterie ; depuis qu elle en a un, ce
n est plus une protection mais un court-circuit. Sans cette trace quelqu un le
reintroduira en croyant combler un trou, comme le plafond de borne lui-meme,
qui etait une bonne idee le jour ou il a ete ecrit.

Golden 105 -> 106. Mutation verifiee ici : plafond de borne remis dans le repli
-> 1 seul echec sur 106, le nouveau scenario, 1130 attendu contre 5725 obtenu.
Il est donc le seul a couvrir ce chemin. Les autres suites intactes
(hysteresis 24, ACM 20, advisor, bilan 48).

⚠️ AUCUN PUT VERS n8n. La production tourne toujours la version poussee a
00:24, sans ce correctif. Le PUT attend la validation du proprietaire.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GwpFpy2aQ2rJok6tNQSbVj
This commit is contained in:
Tallagrand Thierry
2026-09-18 02:29:01 +02:00
parent e8ef252c90
commit 3e4d553a0f
3 changed files with 66 additions and 5 deletions

View File

@@ -443,11 +443,41 @@ const batterySetpointShareW =
) )
: 0; : 0;
// =====================================================
// LE REPLI NE DOIT PAS RESSUSCITER LE DEFAUT (18/09/2026)
// =====================================================
// Quand la garde du bilan tombe -- statut degrade, mesure perimee, champ
// illisible -- la consigne revenait a `maison + plafond de borne`, y compris
// sans voiture. C'est-a-dire au defaut d'origine, et precisement au moment ou
// le systeme est deja degrade. Mesure sur le chemin concerne (midi, fenetre HC
// autorisee, aucun vehicule, bilan indisponible) : 5725 W autorises pour une
// maison a 900 W. Avec la part voiture : 1130 W.
//
// La branche de repli emploie donc la meme part voiture que la branche
// nominale. L'invariant du 01/09 et du 04/09 reste tenu PAR LA FORME : le
// plafond vaut `maison + un terme positif ou nul`, il ne descend donc jamais
// sous la maison, quelle que soit la branche empruntee.
//
// ⚠️ NE PAS REINTRODUIRE UN DISJOINT `nightGridSessionCommitted ||
// forceGridRequested` SUR LA PART VOITURE. Il a ete ecrit, mesure, puis
// retire : il ECRASE le terme batterie du bilan, puisque `max(4825 emprunte,
// 1538 mesure)` retient l'emprunt. Cout mesure, trois attendus figes d'un coup :
// nuit-sans-vehicule-session-engage 2380 -> 5437
// consigne-bilan-fin-de-fenetre-bornee-par-dvcc 2380 -> 5437
// consigne-bilan-plancher-nuage-exclut-la-batterie 4219 -> 6301
// Ce disjoint avait un sens tant que la consigne n'avait AUCUN terme batterie.
// Depuis qu'elle en a un, dimensionne sur le manque reel, il n'est plus une
// protection mais un court-circuit.
//
// Et si l'idee revient malgre tout : pendant une panne de bilan,
// `nightGridSessionCommitted` ne peut engager aucune session NOUVELLE, il ne
// survit que par `committedPrev`. Dans le repli, ce sont donc
// `forceGridRequested` et le terme vehicule qui portent reellement.
const gridSetpointCeilingW = clamp( const gridSetpointCeilingW = clamp(
Math.round( Math.round(
setpointFromBalance setpointFromBalance
? derived.houseNonEvcsW + Math.max(evcsSetpointShareW, batterySetpointShareW) ? derived.houseNonEvcsW + Math.max(evcsSetpointShareW, batterySetpointShareW)
: derived.houseNonEvcsW + derived.physicalCapA * derived.acVoltageRef : derived.houseNonEvcsW + evcsSetpointShareW
), ),
params.gridSetpointIdleW, params.gridSetpointIdleW,
GRID_SETPOINT_MAX_W GRID_SETPOINT_MAX_W
@@ -526,9 +556,10 @@ const batterySurgeBudgetW = Math.max(
// recharge depuis le reseau n'est PAS une exposition : si le solaire s'effondre, // recharge depuis le reseau n'est PAS une exposition : si le solaire s'effondre,
// la recharge ralentit et personne ne se fait couper. L'y inclure remonterait le // la recharge ralentit et personne ne se fait couper. L'y inclure remonterait le
// plancher sans aucune contrepartie de securite, et contre l'objectif. // plancher sans aucune contrepartie de securite, et contre l'objectif.
const gridSetpointLoadW = setpointFromBalance // Une seule branche depuis le 18/09/2026 : le plancher nuage se construit sur
? derived.houseNonEvcsW + evcsSetpointShareW // la maison et la voiture dans les deux cas. La distinction n'avait de sens que
: derived.houseNonEvcsW + derived.physicalCapA * derived.acVoltageRef; // tant que le repli gardait le plafond de borne emprunte.
const gridSetpointLoadW = derived.houseNonEvcsW + evcsSetpointShareW;
const gridSetpointCloudFloorW = Math.round( const gridSetpointCloudFloorW = Math.round(
Math.max(0, gridSetpointLoadW - batterySurgeBudgetW) Math.max(0, gridSetpointLoadW - batterySurgeBudgetW)
); );

View File

@@ -949,6 +949,36 @@ export const scenarios = [
takeoverHelperService: 'turn_off', takeoverHelperService: 'turn_off',
}, },
}, },
// ======== REPLI SANS BILAN : LA BORNE N'EST PLUS EMPRUNTEE (18/09/2026) ========
// Quand la garde du bilan tombe, la consigne revenait a `maison + plafond de
// borne` meme sans voiture -- le defaut d'origine, revenu au moment ou le
// systeme est deja degrade. Ce scenario fige le comportement corrige, et rien
// d'autre dans la suite ne le couvre : sans lui, remettre le plafond brut
// dans la branche de repli laisse les 105 autres scenarios verts.
// ⚠️ L'attendu est calcule sur la FIXTURE, jamais sur une mesure de
// production : maison 900 W + part voiture nulle, plus la marge d'un pas de
// courant. Le rejouer sur l'installation vivante donnerait autre chose sans
// que rien ne soit casse.
{
id: 'consigne-repli-sans-vehicule-n-emprunte-plus-la-borne',
label: "REPLI 18/09 — midi, fenetre HC autorisee, AUCUN vehicule, bilan indisponible : la consigne couvre la maison au lieu d'emprunter le plafond de la borne",
time: MIDDAY,
overrides: {
evcs_vehicle_connected: 'off', evcs_device_connected: 'on',
soc_percent: '50', pv_power_w: '100', pv_power_10m_avg_w: '100',
pv_trend_w: '0', battery_current_a: '-4',
grid_state: 'on', victron_acin1_available: '1',
house_ac_power_w: '900', vebus_ac_out_l1_power_w: '900',
evcs_ac_power_w: '0', evcs_current_a: '0',
grid_setpoint_w: '700',
// Aucun champ `balance_*` : la garde tombe, on est dans la branche de repli.
},
expect: {
profile: 'midday_hc_or_solar',
sourceMode: 'grid_hc_allowed',
gridSetpointW: 1130,
},
},
{ {
id: 'midday-hc-garde-soc', id: 'midday-hc-garde-soc',
label: 'Midi sans PV, SOC 50 > 35, HC midi autorisé + EDF ON → grid proposé mais gardé (soc>35)', label: 'Midi sans PV, SOC 50 > 35, HC midi autorisé + EDF ON → grid proposé mais gardé (soc>35)',

File diff suppressed because one or more lines are too long