# BALAYAGE — « qu'est-ce que ce contrôle aurait attrapé s'il avait échoué ? » *(2026-09-03. La question passée à toute la suite existante. Ce qui suit n'est pas une liste de tests à supprimer : c'est la liste de **ce qu'on croyait vérifié et qui ne l'était pas**.)* ## Pourquoi ce balayage Trois défauts de cette classe ont été rencontrés **sans les chercher**, sous trois formes : | Forme | Cas réel | |---|---| | La contre-épreuve **inerte** | perturber `deliveredWh` sous `unmeasurable`, où il vaut 0 — la perturbation ne pouvait rien changer | | L'identité qui se **referme sur elle-même** | `Σ levels[].targetW == estimatedPowerW` : deux termes de la même source | | Le test qui échoue **à côté** | l'horodatage « après échéance » visait la MÊME échéance — rien ne basculait | Trois formes, **un seul défaut** : le voyant est vert et n'a rien mesuré. ## Résultat 1 — les contrôles qui ne contrôlent rien **Assertions conditionnelles dans une boucle : 0.** La seule qui existait — « sans mesure, pas de `command` » — a été corrigée le 2026-09-01. L'outil (`tools/audit-controles.py`) le confirme et le surveillera. **Identités auto-référentielles : 10 relevées, 1 vaine.** Le motif signale, il ne juge pas — la lecture tranche, et le résultat est très inégal : | Assertion | Verdict | |---|---| | `surplus + recrédit − alloué == restant` | **contrôle** — croise `remainingSurplusW` filé le long de la cascade avec `allocatedW` accumulé en **trois sites distincts**. Attraperait un site qui décrémente sans accumuler | | `lockMin == lockMax`, `minState == maxState` | **contrôlent** — champs différents, ils épinglent le gel d'une charge | | `mapping.size() == levels.size()` | **contrôle** — une dérivation qui produirait un compte différent | | `authorisedW − committedW == remainingW` | **NE CONTRÔLE RIEN** — le moteur l'établit sur **une ligne** ; l'assertion relisait l'affectation | | `remainingWh == targetWh` sous `unmeasurable` | **faible, et documenté** — corollaire de `delivered == false`, pas une garde indépendante | | `expectedW == observedW` sous `device` | **faible, et délibéré** — le mock renvoie sa consigne ; l'assertion documente le piège plutôt qu'elle ne contrôle | **Action** : l'assertion vaine est retirée, **avec sa raison écrite à sa place** — et la distinction d'avec l'identité du budget, qui, elle, mérite d'être assertée. Supprimer sans dire pourquoi aurait invité quelqu'un à la remettre. ## Résultat 2 — les invariants que personne ne couvrait (la moitié qui compte) L'index des règles annonçait **47 règles « spécifiées seules »** — ni code, ni test. Le balayage montre que **l'index mesure la CITATION, pas la couverture** : douze de ces règles étaient couvertes par un test qui ne les nommait pas. | | avant | après | |---|---|---| | citée par un test | 22 | **40** | | spécifiée seule | 47 | **31** | Les liens sont désormais **vérifiables par la machine** au lieu de reposer sur la mémoire de qui a écrit quoi. C'est la doctrine du dépôt appliquée à elle-même : *quelle machine rejouera cette décision ?* **Sur les 31 restantes, 23 appartiennent à des familles NON IMPLANTÉES** — sonde thermique (`ECS-5xx`/`6xx`), transport réseau du §14 (`LM-14xx`), optimiseur externe (`LM-4xx`/`5xx`), ancien lot ECS. Elles sont légitimement sans porteur : écrire leur test aujourd'hui poserait un invariant sur un code que rien n'émet. **Restent huit règles du lot courant sans porteur**, et elles se répartissent en deux tas : - **Cadrage mal classé** — `LM-1000`, `LM-1001`, `LM-1005`, `LM-1006`, `LM-1010`, `LM-1014` : des règles de forme et de doctrine, dont le porteur naturel est la spec elle-même. À reclasser en `cadrage`, ce qui est une correction d'index, pas un manque de test. - **Un vrai manque, assumé** — `LM-1210-a` (la marge retenue est celle de la pire phase, faute de masque de phases). Aucun test ne l'exerce **parce que le mock ne sait pas produire une triphasée déséquilibrée** (cf. `INVENTAIRE_MOCK.md`, manque n°4). C'est le même angle mort, et il est maintenant nommé deux fois. ## Ce que le balayage a coûté et rapporté Coût : une heure. Rapport : **une assertion vaine retirée**, **dix-huit invariants dont la couverture est passée de crue à démontrée**, et **un outil** qui repose la question à chaque passage de `ci-quality`. > **Le résultat le plus utile n'est pas le défaut trouvé, c'est le chiffre corrigé.** On croyait > 47 règles non couvertes ; il y en avait 31, dont 23 légitimes et une seule qui manque vraiment. > Savoir lesquelles change ce qu'on surveille.