feat(ref-optimizer): --capture de la session et mode wrong-type
--capture écrit chaque message reçu et envoyé (NDJSON), token remplacé par autant d'astérisques. wrong-type rend un planId numérique, que le moteur accepte et que le contrat refuse : contre-épreuve de la conformité. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
9322bc8a2f
commit
fc8bc1acd3
@ -61,10 +61,13 @@ Le rejeu compare, cycle par cycle, les actions du créneau courant au plan enreg
|
||||
2.0) · `drop` (ferme la connexion à chaque `GetPlan`) · `unknown-load` · `forbidden-kind` (kind
|
||||
`constraint`) · `missing-load` (une charge disponible absente du créneau courant) · `echo-token`
|
||||
(recopie le token dans `reason` et `planId`) · `protocol-1.0` (optimiseur 1.0 : sans champs 1.1) ·
|
||||
`deviate` (cascade en ordre inverse : doit diverger).
|
||||
`deviate` (cascade en ordre inverse : doit diverger) · `wrong-type` (`planId` numérique : le moteur
|
||||
l'accepte, le contrat OpenRPC le refuse — contre-épreuve de la suite de conformité).
|
||||
|
||||
`--mode-file FICHIER` : le mode est relu dans ce fichier à chaque message — un test change de mode
|
||||
sans reconnexion. `--list-codes` : le catalogue des codes émissibles qu'il embarque (le test
|
||||
sans reconnexion. `--capture FICHIER` : chaque message reçu et envoyé, une ligne JSON-RPC par
|
||||
message — la session que `tools/etm-powersync-schema-check --ndjson` juge contre le contrat ; le
|
||||
token y devient autant d'astérisques (forme gardée, valeur jamais écrite). `--list-codes` : le catalogue des codes émissibles qu'il embarque (le test
|
||||
d'intégration le confronte au moteur).
|
||||
|
||||
Tests : `tests/auto/refoptimizer` (suite `etm-powersync-refoptimizer`, moteur réel + ce script en
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
|
||||
etm-powersync-ref-optimizer [--listen unix:///run/powersync/optimizer.sock | tcp://hôte:port]
|
||||
[--token-file FICHIER] [--mode MODE | --mode-file FICHIER]
|
||||
[--slow-s N] [--horizon-s S]
|
||||
[--slow-s N] [--horizon-s S] [--capture FICHIER]
|
||||
etm-powersync-ref-optimizer --list-codes
|
||||
|
||||
Un serveur JSON-RPC 2.0 sur NDJSON qui répond au `Handshake` et à `GetPlan` par un plan à UN
|
||||
@ -35,6 +35,12 @@ Modes (`--mode`, pour les tests ; un seul à la fois) :
|
||||
protocol-1.0 un optimiseur 1.0 : handshake 1.0 sans `capabilities`, plan sans `validUntil`,
|
||||
`reasonCode`, `reasonParams` ni `diagnostics` (§9 : reste accepté)
|
||||
deviate cascade servie en ordre de rang INVERSE (contre-épreuve : doit diverger)
|
||||
wrong-type `planId` numérique au lieu d'une chaîne : le moteur l'ACCEPTE (il le lit comme
|
||||
texte), le contrat OpenRPC le REFUSE — contre-épreuve de la suite de conformité
|
||||
|
||||
`--capture FICHIER` : chaque message reçu et envoyé, une ligne JSON-RPC par message, dans l'ordre
|
||||
(la session que `tools/etm-powersync-schema-check --ndjson` juge). Le token d'un handshake y est
|
||||
remplacé par autant d'astérisques : sa FORME reste vérifiable, sa valeur n'est jamais écrite.
|
||||
|
||||
`--mode-file` : le mode est relu dans ce fichier à chaque message (un mot ; absent ou illisible =
|
||||
`normal`). Un test change ainsi de mode SANS reconnexion — ce qui distingue « abstention, optimiseur
|
||||
@ -78,7 +84,7 @@ LIGNE_MAX = 1024 * 1024 # E-T2-1 : une ligne de plus de 1 Mio est écar
|
||||
CODE_TOKEN_REFUSE = -32001 # E-T2-6
|
||||
|
||||
MODES = ['normal', 'abstain', 'slow', 'invalid', 'expired', 'wrong-major', 'drop', 'unknown-load',
|
||||
'forbidden-kind', 'missing-load', 'echo-token', 'protocol-1.0', 'deviate']
|
||||
'forbidden-kind', 'missing-load', 'echo-token', 'protocol-1.0', 'deviate', 'wrong-type']
|
||||
|
||||
# Catalogue (energyplugin/etm/types/decisionreason.cpp) : codes qu'un optimiseur peut émettre et
|
||||
# leurs clés de paramètres documentées (INTERFACE.md). Le test d'intégration les confronte au
|
||||
@ -341,6 +347,8 @@ def plan(ctx, mode, horizon_s, token):
|
||||
}
|
||||
if mode == 'invalid':
|
||||
resultat['slots'] = 'mode invalid (test) : slots n\'est pas une liste'
|
||||
elif mode == 'wrong-type':
|
||||
resultat['planId'] = 42
|
||||
elif mode == 'protocol-1.0':
|
||||
# Ce qu'un optimiseur 1.0 savait dire : identifiant, stratégie, créneaux, actions et texte.
|
||||
for champ in ('validUntil', 'computedAt', 'diagnostics'):
|
||||
@ -375,10 +383,31 @@ def lire_token(chemin):
|
||||
return '' # configuré mais illisible : rien ne correspond
|
||||
|
||||
|
||||
VERROU_CAPTURE = threading.Lock()
|
||||
|
||||
|
||||
def capturer(cfg, message):
|
||||
"""--capture : une ligne par message. Le token reçu au handshake, et toute recopie du token
|
||||
configuré (mode echo-token), deviennent autant d'astérisques : forme gardée, valeur jamais écrite."""
|
||||
if not cfg.capture:
|
||||
return
|
||||
if isinstance(message, dict) and isinstance(message.get('params'), dict) \
|
||||
and isinstance(message['params'].get('token'), str):
|
||||
message = dict(message, params=dict(message['params'], token='*' * len(message['params']['token'])))
|
||||
ligne = json.dumps(message, separators=(',', ':'), ensure_ascii=False)
|
||||
token = lire_token(cfg.token_file)
|
||||
if token:
|
||||
for forme in (token, json.dumps(token, ensure_ascii=False)[1:-1]):
|
||||
ligne = ligne.replace(forme, '*' * len(forme))
|
||||
with VERROU_CAPTURE, open(cfg.capture, 'a', encoding='utf-8') as f:
|
||||
f.write(ligne + '\n')
|
||||
|
||||
|
||||
class Connexion(socketserver.StreamRequestHandler):
|
||||
"""Une connexion = un fil ; requêtes traitées dans l'ordre d'arrivée."""
|
||||
|
||||
def envoyer(self, message):
|
||||
capturer(self.server.cfg, message)
|
||||
ligne = json.dumps(message, separators=(',', ':'), ensure_ascii=False).encode() + b'\n'
|
||||
self.wfile.write(ligne)
|
||||
self.wfile.flush()
|
||||
@ -402,6 +431,7 @@ class Connexion(socketserver.StreamRequestHandler):
|
||||
except ValueError:
|
||||
journal('ligne non JSON écartée')
|
||||
continue
|
||||
capturer(cfg, message)
|
||||
if not isinstance(message, dict) or 'method' not in message:
|
||||
continue
|
||||
ident, methode = message.get('id'), message.get('method')
|
||||
@ -497,6 +527,7 @@ def main(argv=None):
|
||||
p.add_argument('--slow-s', type=float, default=12.0, help='délai du mode slow, en s (défaut 12)')
|
||||
p.add_argument('--horizon-s', type=int, default=300,
|
||||
help='durée du créneau et de validUntil, en s (défaut 300)')
|
||||
p.add_argument('--capture', help='écrit chaque message reçu et envoyé dans ce fichier (NDJSON)')
|
||||
p.add_argument('--list-codes', action='store_true',
|
||||
help='écrit le catalogue des codes émissibles (JSON) et sort')
|
||||
args = p.parse_args(argv)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user