Patrick Schurig ccf05cea87 feat(R6): la mesurabilité se déclare à la saisie, et l'inventaire du mock
Le signal vit dans GetLoadConfig, en r:, et non dans la télémétrie.
`measurement` n'existe que pour les charges arbitrées ; or une charge qu'on
DÉCLARE est précisément celle qui n'y est pas encore. L'écran serait muet au
moment exact où il doit parler — celui de la saisie, avant qu'une obligation
n'achète à l'aveugle tous les jours.

Trois causes, trois gestes : noMeter se répare en une manipulation,
meterWithoutEnergy demande de remplacer le compteur, noSessionEnergy ne se
répare pas. Sans la cause, l'avertissement ne peut dire que « ça ne marchera
pas ».

Et meterThingId ne permet pas de le déduire : un compteur peut publier
currentPower sans totalEnergyConsumed — mesuré sur l'ECS du banc, reproduit
au test avec un powerSwitch désigné comme compteur. La puissance ne suffit
pas, il faut une énergie. Contre-épreuve : déduction par meterThingId seul,
le cas du banc passe pour mesurable.

INVENTAIRE À FROID DU MOCK. Trois fois en trois jours qu'un cas réel s'est
révélé inatteignable ; ce n'est plus un incident, c'est une propriété du banc
— il est plus homogène que la réalité, ayant été construit pour faire marcher
des scénarios et non pour représenter la diversité du parc.

Quatre cas restent structurellement introuvables, dont `source: none` pour
une charge SAINE — toutes les classes pilotables portent une puissance, si
bien qu'une charge non mesurée mais en bon état est impossible à fabriquer,
alors qu'un relais sec est banal sur le terrain. Et la branche `temperature`,
écrite et jamais exécutée faute d'une classe qui porte l'état.

La règle : avant d'écrire un test qui exerce une ABSENCE, vérifier que le
mock peut la produire. Une assertion sur un cas inatteignable ne passe pas,
elle NE S'EXÉCUTE PAS — ce qui est pire, car elle compte comme verte. Le
symptôme se reconnaît : la contre-épreuve ne mord pas.

Deux ajouts dans AGENTS.md. L'asymétrie des latences est une PROPRIÉTÉ, pas
une chance : le refus qui coûte de l'argent est celui de la descente, et
c'est le plus vite constaté — ce qui n'est vrai que parce que le seuil vient
du mécanisme. Et la question opératoire appliquée aux trois autres identités,
avec un résultat inégal qui est le but : celle de `draw` repose sur le fait
que le compteur voie toute la maison, ce que rien ne vérifie ;
Σ levels[].targetW == estimatedPowerW est interne au plan et ne dit rien du
matériel — il a fallu un champ hors de la formule pour relier les deux.

Suite : simulation 61/61, loadmodel 22/22, charging 48/48 identique à la
référence, spotmarket 32/32, doxygen 0 avertissement.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015F7G5VeaPVSMeVNjiGj36p
2026-09-01 17:27:56 +02:00

4.4 KiB

INVENTAIRE À FROID DU MOCK — ce qu'il rend structurellement introuvable

(Fait le 2026-09-01, après trois occurrences en trois jours d'un cas réel impossible à reproduire. Ce n'est plus un incident, c'est une propriété du banc d'essai.)

Pourquoi cet inventaire

Trois fois cette semaine, un défaut réel s'est révélé inatteignable par la suite :

Occurrence Ce qui était introuvable Comment on l'a su
Correctif amont calculateAllowanceAmpere un compteur publiant les courants sans les puissances par phase le mock pose currentPhaseB = currentPowerPhaseB / 230, cohérent par construction
Test command sous source: none une charge dont rien ne mesure la puissance toutes les classes pilotables portent currentPower
Test ECO_FLOOR_* en régime unmeasurable une borne sans sessionEnergy il a fallu ajouter l'état à une seule classe pour avoir les deux cas

Le point commun : le mock est plus homogène que la réalité. Il a été construit pour faire marcher des scénarios, pas pour représenter la diversité du parc — et une suite verte ne dit alors rien sur les cas qu'il ne sait pas produire.

Les classes, et ce qu'elles portent

Classe currentPower totalEnergyConsumed sessionEnergy
meter ✅ ✅ —
charger ✅ ✅ ❌
chargerPhaseSwitching ✅ ✅ ✅ (ajouté le 2026-08-31)
simpleCharger ❌ ❌ ❌
powerSwitch ✅ ❌ —
etmVariableLoad ✅ (currentPowerW) ❌ —
energyStorage ✅ ❌ —
car, notification ❌ ❌ —

Ce que la table donne, et c'est la bonne nouvelle : sessionEnergy et totalEnergyConsumed sont désormais discriminants — il existe des classes avec et sans. Les régimes measured / unmeasurable et les causes noSessionEnergy / meterWithoutEnergy sont donc exerçables.

Ce qui reste STRUCTURELLEMENT introuvable

1. measurement.source: none pour une charge saine. Toutes les classes pilotables portent une puissance. Le seul chemin est une charge dont le Thing n'existe pas (câblage manquant) — cas réel, mais qui traîne avec lui available: false. Une charge saine et non mesurée est impossible à fabriquer ici, alors qu'elle est banale sur le terrain (un relais sec).

2. Un compteur par phase incohérent. currentPhaseX et currentPowerPhaseX sont posés ensemble par le mock, dans un rapport fixe de 230. Aucun scénario ne peut produire une tension réelle ≠ 230 V, ni un compteur qui ne publierait qu'une des deux familles.

3. temperature. Le code teste hasState("temperature") et aucune classe du mock ne le porte : la branche « sonde présente » n'a jamais été exécutée. Elle est écrite, elle n'est pas testée.

4. Une borne triphasée à phases déséquilibrées. Les états existent, mais aucun helper de test ne les pose indépendamment — d'où le pessimisme de LM-1210-a qui n'a jamais été exercé sur un vrai déséquilibre.

La règle qui en sort

Avant d'écrire un test qui exerce une ABSENCE, vérifier que le mock peut la produire. Une assertion sur un cas inatteignable ne passe pas : elle ne s'exécute pas, ce qui est pire — elle compte comme verte.

Le symptôme se reconnaît : la contre-épreuve ne mord pas. C'est le corollaire fin d'AGENTS.md appliqué au banc d'essai plutôt qu'au code.

Et la parade tient en une ligne de test : quand on itère pour trouver des cas, compter combien on en a examinés, et échouer si le compte est nul. C'est ce qui a rattrapé le test de command, et ce qui garde celui de la mesurabilité.

Ce qu'il faudrait ajouter, par ordre d'utilité

  1. Une classe pilotable SANS aucune puissance — un relais sec. Débloque source: none sur une charge saine, et sépare enfin « non mesurée » de « en panne ».
  2. Un compteur sans totalEnergyConsumed en tant que classe déclarée, plutôt qu'un powerSwitch détourné comme aujourd'hui.
  3. Une tension par phase réglable indépendamment des puissances — pour exercer le correctif amont ailleurs qu'en inversant la fonction.

Aucune de ces trois n'est urgente. Ce qui l'est, c'est de savoir qu'elles manquent : sans cette liste, chaque lot repaiera le même temps à découvrir qu'un cas est introuvable.