3 Commits

Author SHA1 Message Date
Patrick Schurig
14b86de3c9 trace : une requête non envoyée n'est tracée que par sa taille (Q7)
Avec les traces actives, la ligne d'un cycle recopiait la requête GetPlan
même quand le client ne l'avait pas écrite (ligne de plus de 1 Mio,
request-too-long) : environ 1 Mio par cycle jusqu'au plafond journalier,
pour une requête que personne n'a reçue, et que le rejeu aurait renvoyée.

- La ligne porte alors `requestBytes` (taille de la ligne, octets) à la
  place de `request` ; sa réponse reste l'erreur request-too-long. Même
  règle pour la requête qui SERAIT partie sans optimiseur.
- La décision « part ou ne part pas » est celle du client :
  OptimizerClient::getPlanLine(), extraite de requestPlan() sans changer
  son comportement, est lue par la trace — pas une seconde estimation.
- Le commentaire « la requête RÉELLEMENT envoyée » est corrigé, la doc de
  la ligne de trace (optimizertrace.h) aussi.

Preuve : testARequestNotSentIsTracedBySizeOnly (avec et sans optimiseur ;
contre-épreuve : la requête envoyée est recopiée).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 20:26:26 +02:00
Patrick Schurig
693e01579f fix(optimiseur): un token recopié par l'optimiseur ne sort par aucun journal (brief 4 T7, correctif A)
Constat de T6 (question 2) : la trace masquait un token recopié dans reason ou planId, mais
les lignes [Optimiseur] (planId, texte, détail des refus, nom du handshake) et
GetOptimizerStatus.name le portaient en clair.

- OptimizerSecret : UNE fonction de masquage (texte brut et échappé JSON, objet JSON à toute
  profondeur, clés comprises ; ligne de trace sérialisée), partagée par le client et les traces.
- OptimizerClient masque chaque message reçu dès son analyse, avec le token envoyé sur cette
  connexion : rien en aval (journal, traces, app) ne voit le secret ; dit une fois.
- Test testAnEchoedTokenLeaksNowhere : secret avec " et \ recopié partout ; aucune ligne de
  journal (tous niveaux), aucune trace, aucune réponse RPC. 4 contre-épreuves mordent.

decisions sans endpoint : IDENTIQUE (23 792 lignes), 0 rejeu.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-17 13:10:15 +02:00
Patrick Schurig
483bbcb027 feat(traces): traces de décision NDJSON, une ligne par cycle (brief 4 T6, annexe D)
Réglage optimizerTrace (energy.conf Optimizer/trace, …RetentionDays, …MaxBytesPerDay), défaut
DÉSACTIVÉ : ni objet, ni fil, ni fichier. NymeaEnergy.GetOptimizerTrace / SetOptimizerTrace,
PermissionScopeAdmin comme SetOptimizerEndpoint, type contrôlé par le moteur, appliqué à chaud.

Une ligne par cycle — rule-based, optimiseur, et L2 (strategy none, sans requête) : t (heure
locale), request (params GetPlan envoyés, ou qui l'auraient été), response (ce qui a répondu
à CETTE requête : result brut, error.code, no-response ; sinon le plan du rule-based en codes),
executed (appliqué, clamped, shed), strategy, latencyMs. La ligne N attend la réponse à la
requête N (elle sert au cycle N+1).

Toutes les E/S sur un fil dédié (OptimizerTraceWriter) : rotation à minuit heure locale, purge
au-delà de la rétention (14 j), plafond par fichier journalier (64 Mio, mesuré : 5 Ko/ligne
rule-based, 39 Ko sous un optimiseur à 48 créneaux, 1 cycle/min), file bornée à 256 lignes.
Échec d'écriture et plafond dits une fois. update() ne fait que poster. Le token n'est jamais
dans une ligne ; recopié par une réponse d'optimiseur, il est masqué.

Preuves : decisions IDENTIQUE traces désactivées ET activées (21 132 lignes de trace écrites,
ETM_DECISIONS_TRACES=1). socketscheduler 33 fonctions, 114/0 (+10 fonctions T6).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-17 12:12:25 +02:00