Patrick Schurig d961b73ed7 Validateur du contrat : juge error.data selon la version de l'optimiseur (protocole 1.3)
tools/etm-powersync-schema-check :
- lit methods[].errors[].x-etm-data-schema : error.data d'une réponse error de ce code est jugé
  contre ce schéma (audité comme les autres) ; exigé à partir de x-etm-data-required-from, la
  version étant celle que l'optimiseur annonce dans SON Handshake ; en deçà, écart écrit en
  AVERTISSEMENT, sans effet sur le code de sortie ; version inconnue = celle du contrat ;
- truncated n'accompagne que 50 problèmes exactement ;
- aucune valeur reçue recopiée dans error.data : écart dès 1.2 (règle de 1.2). Le contrôle ne
  porte que sur les valeurs qu'un chemin conforme ne peut pas contenir — il ne peut pas accuser
  un chemin conforme (domain « battery » / params.battery) ;
- --error (un objet error, avec --request et --handshake) et --responses-only (session de sonde,
  requêtes fautives à dessein) ; RÉSUMÉ compte les avertissements.

Tests : forme exigée en 1.3 et avertie en 1.2 sur de vraies sessions de l'optimiseur de
référence (normal, old-data, echo-value × 1.3, 1.2), forme ligne à ligne, recopie sans faux
positif, contre-épreuve « sans x-etm-data-schema, rien n'est jugé ». Les cinq nouveaux tests
échouent avec le validateur précédent.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 11:13:57 +02:00

95 lines
5.8 KiB
Markdown

# tools/
Outils de développement et d'exploitation du plugin. Ce fichier décrit
`etm-powersync-trace-replay` et les deux outils du contrat du protocole optimiseur ;
l'optimiseur de référence a le sien (`etm-powersync-ref-optimizer/README.md`), les autres scripts
portent leur mode d'emploi en en-tête.
Noms d'optimiseur : `powersync-optimizer` = exemple de référence GPL (ce dépôt) ; `etm-optimizer` = optimiseur ETM (propriétaire, hors de ce dépôt). L'optimiseur de
référence s'annonce `powersync-optimizer` au handshake ; son fichier garde le préfixe des artefacts.
## `etm-powersync-trace-replay` — rejouer des traces de décision
Python 3, bibliothèque standard seule, GPL-3.0 comme le dépôt.
Le moteur écrit, quand le réglage `optimizerTrace` est activé (`NymeaEnergy.SetOptimizerTrace`,
défaut : désactivé), une ligne NDJSON par cycle dans
`<storagePath>/etm-powersync-optimizer-trace-AAAA-MM-JJ.ndjson` (format :
`INTERFACE.md`, « Traces de décision » ; protocole : `docs/OPTIMIZER_PROTOCOL.md` annexe D).
L'outil renvoie chaque `request` d'un de ces fichiers à un optimiseur et compare ses actions au
plan enregistré.
```sh
# optimiseur local (défaut : unix:///run/powersync/optimizer.sock)
./tools/etm-powersync-trace-replay etm-powersync-optimizer-trace-2026-10-17.ndjson
# optimiseur sur le LAN, avec token, échec si une action diffère
./tools/etm-powersync-trace-replay trace.ndjson --endpoint tcp://nas:7600 \
--token-file ~/.config/etm-optimizer.token --fail-on-divergence
```
- **Token** : seulement par `--token-file` (jamais en argument : il se verrait dans `ps`).
Il n'est ni affiché ni écrit.
- **Sorties** : `<trace>.replay-<horodatage>.ndjson` (une ligne par requête rejouée : réponse,
latence, écarts) et `….summary.txt`, à côté de la trace. Jamais d'écrasement (`--out` vers un
fichier existant est refusé).
- **Comparaison** : charge, kind, puissance d'une consigne (`setpoint`, à `--tolerance-w` près),
état d'une action `state` — la puissance d'une action `state` n'est pas comparée (le §6 ne la
définit pas, et le plan du rule-based tracé la porte).
- **Résumé** : requêtes rejouées, taux d'abstention, `computeMs` médian et max (s'il est fourni
dans `diagnostics`), cycles non comparables (réponse enregistrée en erreur), cycles dont les
actions du créneau courant diffèrent du plan enregistré, avec la liste.
- **Code de sortie** : 0 ; 1 avec `--fail-on-divergence` s'il y a divergence ; 2 sur erreur
(fichier, connexion, handshake refusé).
- Les lignes L2 (`strategy: "none"`) n'ont pas de requête : elles ne sont pas rejouées.
Les traces contiennent des mesures du site : elles **restent sur le site**. L'export est manuel
(copie du fichier par l'installateur) ; l'outil ne parle qu'à l'endpoint donné.
Test : `python3 -I tests/tools/etm-powersync-trace-replay-test.py` (serveur de test minimal qui
renvoie les réponses enregistrées — ce n'est pas l'optimiseur de référence).
## Le contrat du protocole optimiseur — `etm-powersync-schema-check`, `etm-powersync-gen-protocol-doc`
Python 3, bibliothèque standard seule, aucun accès réseau. Le contrat :
`docs/etm-powersync-optimizer-protocol.openrpc.json` (OpenRPC 1.3), servi par le moteur
(`NymeaEnergy.GetOptimizerProtocol`).
```sh
# un message, une session NDJSON (les deux sens), des traces de décision
./tools/etm-powersync-schema-check --method GetPlan --params requete.json
./tools/etm-powersync-schema-check --method GetPlan --result plan.json --request requete.json --semantic
./tools/etm-powersync-schema-check --ndjson session.ndjson --semantic
./tools/etm-powersync-schema-check --ndjson sonde.ndjson --responses-only # requêtes fautives à dessein
./tools/etm-powersync-schema-check --method GetPlan --error erreur.json --request requete.json \
--handshake handshake.json # error.data d'un -32602 (protocole 1.3)
./tools/etm-powersync-schema-check --trace etm-powersync-optimizer-trace-2026-10-17.ndjson --semantic
./tools/etm-powersync-schema-check --subset # le sous-ensemble de JSON Schema géré
# la prose du protocole, régénérée depuis le contrat ; --check : code 1 si pas à jour (ci-quality 5/5)
./tools/etm-powersync-gen-protocol-doc [--check]
# les listes fermées du contrat, régénérées depuis le CODE
ETM_POWERSYNC_CODE_LISTS=/tmp/listes.json <build>/tests/auto/protocolschema/etm-powersync-protocolschema \
testClosedListsAreGeneratedFromTheCode
./tools/etm-powersync-gen-protocol-doc --enums-from /tmp/listes.json
```
- **Sous-ensemble géré** : `$ref` local, `type`, `enum`, `const`, `properties`, `required`,
`additionalProperties`, `propertyNames`, `items`, `minItems`, `maxItems`, `minimum`, `maximum`,
`exclusiveMinimum`, `minLength`, `maxLength`, `pattern`, `oneOf`, `anyOf`, `allOf`. Tout autre
mot-clé est une erreur de schéma (code 2), jamais ignoré.
- **`--semantic`** applique `x-etm-semantic-rules` : ce que le validateur du moteur refuse en plus de
la forme, dans son ordre, sous le code qu'il publie (`fallbackCode`).
- **`error.data`** (protocole 1.3) : la forme d'`error.data` d'une erreur, quand le contrat la norme
(`x-etm-data-schema`, `-32602` de `GetPlan`), est exigée d'un optimiseur qui s'annonce
`x-etm-data-required-from` ou plus, et seulement AVERTIE en deçà (ligne `AVERTISSEMENT`, sans effet
sur le code de sortie) ; une valeur reçue recopiée dans `error.data` est un écart dès 1.2.
- **Code de sortie** : 0 conforme ; 1 écart de forme ou refus sémantique ; 2 erreur d'usage ou de
schéma.
- Conformité d'un optimiseur de bout en bout, avec le vrai moteur : suite
`etm-powersync-optimizer-conformance` (`tests/auto/conformance/README.md`).
Tests : `python3 -I tests/tools/etm-powersync-schema-check-test.py` et
`python3 -I tests/tools/etm-powersync-gen-protocol-doc-test.py`.