3 Commits

Author SHA1 Message Date
Patrick Schurig
b558df80b0 tools: l'index des règles, généré — et son garde-fou
Étape 2 du chantier de documentation. tools/gen-rules.py produit
doc-generated/RULES.md : chaque règle, son énoncé d'une ligne, son domicile, son
état de preuve DÉRIVÉ, et la liste de ce qui la cite. Sort en erreur sur un
identifiant cité et jamais défini, sur un identifiant qui n'existe qu'en ligne de
tableau, et sur un domicile ambigu.

L'état de preuve ne demande AUCUNE convention nouvelle : il se lit dans l'endroit
où l'identifiant apparaît — energyplugin/ pour le code, tests/ pour les tests,
docs/RELEVE_* pour la machine. Citer les règles en commentaire est déjà
l'habitude de la maison, et c'est cette habitude qui devient l'index.

« cité par », jamais « ✅ » : cet index prouve la CITATION, pas la couverture. Un
✅ automatique serait le même mensonge que celui qu'on corrige, en plus rapide.
C'est écrit en tête de la page générée, pas seulement dans un commit.

DEUX MESURES PRÉALABLES ÉTAIENT FAUSSES, et le générateur le montre. Un
identifiant admet des suffixes ALPHABÉTIQUES et NUMÉRIQUES — LM-1006-1,
LM-1105-b, LM-1209-a-bis. Un tiret ordinaire ne peut donc pas séparer
l'identifiant de l'énoncé : « **LM-1006-1** (…) », une citation, se lisait comme
une définition concurrente de LM-1006. D'où le séparateur cadratin, et d'où la
disparition des « 49 orphelins » et des « 20 domiciles ambigus » annoncés :
il en reste DIX, et ce sont les mêmes dans les deux catégories.

Et la première sortie a trouvé une faiblesse dans les citations écrites hier :
LES SOUS-RÈGLES N'HÉRITENT DE RIEN. Citer LM-1105 ne marque pas LM-1105-b, qui
sortait « spécifiée seule » alors qu'elle est implémentée et testée. Corrigé aux
quatre endroits concernés — la règle de convention est désormais : citer
l'identifiant EXACT, jamais le parent.

Aucun changement de comportement : les quatre citations touchées sont des
commentaires. Build 0/0, testMeasurementSourceIsPublished 3/3.

doc-generated/ reste ignoré par git, comme le reste de la sortie de gen-doc.sh :
versionner une page générée ferait un diff à chaque édition de spec. La garantie
qu'elle est à jour appartient au contrôle CI (étape 4).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015F7G5VeaPVSMeVNjiGj36p
2026-08-28 16:08:22 +02:00
Patrick Schurig
5229fdb06e feat(lot D): libellé jusqu'à l'adaptateur, relais comparés par table, paliers publiés
Les trois premiers constats de BRIEF_agent_plugin.md, dans l'ordre de ce qu'ils coûtent.

§2 — Renommer une charge n'atteignait jamais l'adaptateur. updateSoftConfig() ne
portait que priority et needs, donc un changement de libellé seul était compté
« inchangée » : l'adaptateur gardait son ancien nom et le journal contredisait
GetLoadConfig jusqu'à la prochaine reconstruction. `label` entre dans la signature et
dans le prédicat soft de l'arbitre, sans entrer dans sameHardware() — mise à jour EN
PLACE, pas reconstruction. La vraie prise était le Q_UNUSED de SgReadyAdapter, hérité
d'avant le lot B-bis : sans lui, renommer la PAC — le cas même qu'on allait tester —
n'aurait rien changé, et on aurait conclu que le correctif ne marchait pas.

§3 — relays[] n'est plus comparé par index. Un réordonnancement reconstruisait la
charge : contacts ouverts, verrous réarmés à froid. Comparer comme un ENSEMBLE aurait
été faux dans l'autre sens — à somme égale la table retient la première combinaison
rencontrée, donc échanger deux relais de même puissance change le contact qui sert ce
palier. sameHardware() compare la TABLE DÉRIVÉE : deux listes qui produisent les mêmes
paliers avec les mêmes contacts sont matériellement identiques, par construction.

§4 — mechanism.stagesW publie les paliers atteignables, en télémétrie et pas dans
GetLoadConfig. La télémétrie n'a aucun chemin d'écriture, donc « lecture seule » y est
vrai par construction ; GetLoadConfig aurait cassé l'aller-retour neutre dont l'app
dépend pour écrire — elle renvoie verbatim ce qu'elle a lu, le champ serait revenu
dans SetLoadConfig, et un refus en bloc aurait emporté toutes les charges.

deriveStages() est l'implémentation unique de la combinatoire, partagée par les trois
usages. C'était le fond de la demande du §4 : une seule règle, pas de divergence.

tests/auto/loadmodel : cible neuve sans serveur nymea, 2 ms. Elle a attrapé un défaut
de la correction elle-même avant le banc — operator== comparait les listes de ThingIds
et voyait une différence entre fermer {K1,K2} et fermer {K2,K1}.

tools/repair_energylogs.py : §1, et ce n'est PAS un correctif de ce plugin. La cause
est extérieure et déjà corrigée (SunSpec float32 aux mots permutés, d08cf50 daté
05:30:14 UTC — la seconde du premier échantillon empoisonné). La séquelle, elle, était
définitive : à 7,5e33 kWh le plus petit incrément représentable vaut 1,67e18, donc le
compteur n'était pas faux mais GELÉ, et rechargé tel quel à chaque démarrage.
Appliqué sur .75 : 62 726 anomalies → 0, accumulation reprise et vérifiée en direct.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XPUo3RMr8SzK6qbFtfBm8H
2026-08-26 10:07:22 +02:00
Patrick Schurig
941795410b docs(doxygen): Graphviz activé + cible de génération reproductible
HAVE_DOT = YES, mais CALL_GRAPH et CALLER_GRAPH restent à NO, et pas pour une question
de coût : le moteur est signal-driven. powerBalanceChanged → verifyOverloadProtection()
est une connexion Qt, invisible à l'analyse statique ; un graphe d'appel montrerait
update() comme un point d'entrée orphelin et raterait le mécanisme principal de la
couche L4. Un schéma faux vaut moins que pas de schéma.

Ce que dot apporte réellement : héritage, collaboration, inclusions. SVG, lisible à toute
échelle et diffable, avec DOT_GRAPH_MAX_NODES = 60 pour éviter les pavés illisibles.

tools/gen-doc.sh : une seule commande, le Doxyfile épinglé. C'est la leçon du piège des
générateurs — 163 avertissements sans sortie contre 113 avec HTML+XML sur le même arbre.
Un chiffre de référence ne vaut que rattaché à une configuration exacte, et une commande
unique empêche qu'on régénère « à sa façon ». Le script signale aussi une version de
doxygen différente de la référence 1.9.8.

Avertissement ajouté en tête du Doxyfile, à côté de celui sur les générateurs : le graphe
d'héritage sera tronqué. EnergyArbitrator : public SmartChargingManager, mais l'amont est
hors INPUT. C'est voulu — élargir INPUT ferait exploser le compte d'avertissements. Sans
cette note, quelqu'un le « réparera ».

graphviz est documenté comme dépendance de la cible doc, pas du build : build-cross-arm64
compile, il ne dessine pas.

Hors de ce lot, comme convenu : \defgroup, carte \dot cliquable, WARN_AS_ERROR = YES,
hébergement.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-10 06:39:15 +02:00