Il s'annonce 1.2 et s'aligne sur les règles écrites par ce lot :
- Q3 : GetPlan avant un handshake réussi → -32600 (était -32002, hors norme),
connexion gardée ; Handshake d'une autre majeure → -32602 puis fermeture
(il ne comparait pas la majeure du moteur) ; token refusé → -32001, inchangé.
- Q2 : un timestamp illisible ou sans décalage n'est plus une abstention mais
-32602, error.data.problems = [{path, problem}], jamais la valeur reçue.
- Q7 : une ligne de plus de 1 Mio ('\n' non compté) reçoit -32600 à id null.
Correctif au passage : readline(LIGNE_MAX + 2) rendait le reste d'une
ligne trop longue comme des lignes suivantes, et comptait le '\n' (une
ligne d'exactement 1 Mio était refusée). La ligne est désormais lue jusqu'à
sa fin par morceaux, sans être gardée.
- Q8 : un lot reçoit -32600 à id null, aucun de ses messages n'est traité
(il était ignoré en silence).
- -32700 à id null pour une ligne non JSON (ignorée en silence avant).
- Q5 et Q6 étaient déjà tenus : token accepté sans --token-file,
UpdateLearned en notification ignorée, avec id → -32601.
README : tableau des erreurs.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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é.
# 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 dansps). 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 (--outvers un fichier existant est refusé). - Comparaison : charge, kind, puissance d'une consigne (
setpoint, à--tolerance-wprès), état d'une actionstate— la puissance d'une actionstaten'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,
computeMsmédian et max (s'il est fourni dansdiagnostics), 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-divergences'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).
# 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 --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é :
$reflocal,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é. --semanticappliquex-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).- 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.