From 3f5f6e0d9f6ac33b4b38b9857904e9ac172ed238 Mon Sep 17 00:00:00 2001 From: Patrick Schurig Date: Sat, 29 Aug 2026 10:44:14 +0200 Subject: [PATCH] =?UTF-8?q?docs(correction):=20mon=20=C2=AB=20=C3=A9cho=20?= =?UTF-8?q?refus=C3=A9=20=C2=BB=20=C3=A9tait=20un=20=C3=A9cho=20P=C3=89RIM?= =?UTF-8?q?=C3=89=20=E2=80=94=20deux=20cas,=20un=20sympt=C3=B4me?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit L'agent plugin a mesuré l'inverse sur la même box : écho verbatim, "auto" compris, EnergyErrorNoError. L'exception existe déjà côté moteur — reconcileRankOrigin() accepte "auto" quand le persisté le porte déjà, écrite exprès pour ne pas rompre la neutralité Get → Set. Ce que j'avais mesuré est autre chose : entre ma lecture et mon écriture, la marque avait été consommée par une manipulation concurrente. Mon écho portait donc un "auto" que le persisté ne portait plus — un écho périmé, correctement refusé. Deux cas distincts, un seul symptôme, et c'est précisément pourquoi une écriture qui ne relit pas juste avant ne prouve rien. Le contrôle de la suppression, lui, relit avant de conclure ; ma sonde jetable, non. §10 corrigé : la ligne du tableau dit maintenant ce qu'elle mesure vraiment, et ce qui reste à mesurer (un écho inchangé sur une marque FRAÎCHE). sansRankOriginAuto() est documentée comme un contournement EN SURSIS — elle part au résultat de cette mesure, et la marque survivra alors aux gestes qui ne la concernent pas. Rien n'est retiré d'ici là. Ce qui reste vrai de mon relevé : "user" s'écrit, ne déplace pas le rang, survit aux gestes voisins. Et l'effacement de "auto" que j'ai observé est le fait de l'APP — de son nettoyage — pas d'un refus de la box. ── Deux pièges de sonde, consignés dans tools/rpc/README.md ───────────────── 1. Un refus arrive en `status: "success"`, verdict dans params.energyError. Une sonde qui ne teste que `status` annonce « accepté » sur une écriture rejetée. 2. Les identifiants ne s'écrivent pas pareil d'un appel à l'autre — le §9, documenté la VEILLE, et la sonde y est quand même tombée : elle rendait des verdicts sur une charge qu'elle ne regardait pas. La leçon n'est pas « écrire des sondes plus soigneusement » : c'est que la sonde ne partage pas les garde-fous de l'app. Quand la question porte sur ce que l'app fera, importer son modèle vaut mieux que relire le JSON ; quand elle porte sur la box, croiser la réponse RPC avec journalctl avant de conclure. Et la raison lisible d'un refus n'existe QUE dans ce journal — côté RPC il ne reste qu'un code. Silence de refus ouvert depuis le premier jour, rencontré une fois de plus, et raison d'être des miroirs clients comme validateSet. Rien de touché côté entrelacement : ça attend le moteur. 226 tests inchangés, analyze inchangé à 27. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01SQbZKrWqsMFP1Lh2jjjd9f --- docs/BRIEF_plugin_depuis_maquettes.md | 7 +++++++ docs/UI_data_contract.md | 21 +++++++++++++++++---- lib/services/load_priority.dart | 14 +++++++++----- tools/rpc/README.md | 27 +++++++++++++++++++++++++++ 4 files changed, 60 insertions(+), 9 deletions(-) diff --git a/docs/BRIEF_plugin_depuis_maquettes.md b/docs/BRIEF_plugin_depuis_maquettes.md index 6015191..137acfa 100644 --- a/docs/BRIEF_plugin_depuis_maquettes.md +++ b/docs/BRIEF_plugin_depuis_maquettes.md @@ -54,6 +54,13 @@ sortie unique (`NymeaService.setLoadConfig`). L'écriture passe, mais la marque la box » disparaîtra donc d'une borne que personne n'a classée, au premier renommage d'une autre charge. +> ✅ **Répondu le 2026-08-29 : l'exception existe déjà.** `reconcileRankOrigin()` accepte +> `"auto"` quand le persisté le porte déjà. Le refus mesuré côté app portait un écho +> **périmé** — la marque avait été consommée entre la lecture et l'écriture — ce qui est le +> bon comportement, et un symptôme identique pour deux causes distinctes. La mesure sur +> marque fraîche est en cours côté moteur ; à son résultat, `sansRankOriginAuto()` disparaît +> côté app. **Tout ce qui suit reste comme trace de la demande, et de son argument.** + **La demande** : tolérer l'écho **inchangé** — même id, même rang, même valeur. Le refus vise à empêcher un client de **fabriquer une affirmation** sur un geste du moteur. diff --git a/docs/UI_data_contract.md b/docs/UI_data_contract.md index 9bcf868..87a537d 100644 --- a/docs/UI_data_contract.md +++ b/docs/UI_data_contract.md @@ -453,14 +453,27 @@ Une seconde wallbox simulée appairée après `+etm27` a produit les trois cas d | Écriture | Réponse de la box | État après | |---|---|---| -| renommage d'une AUTRE charge, `"auto"` renvoyé verbatim | `status: "success"` + `energyError: "EnergyErrorInvalidParameter"` | **inchangé** | +| renommage, `"auto"` renvoyé **alors que le persisté ne le portait plus** | `status: "success"` + `energyError: "EnergyErrorInvalidParameter"` | **inchangé** | | même renommage, `"auto"` → `""` (ce que fait l'app) | `EnergyErrorNoError` | **la marque est effacée** | | `"user"` sur la seule borne visée | `EnergyErrorNoError` | rang inchangé, seul ce champ change | | deux renommages voisins ensuite | `EnergyErrorNoError` | **`"user"` survit** | -Le coût annoncé est donc réel et vérifié : le badge « rang posé par la box » a disparu d'une -borne que personne n'avait classée, au renommage d'une autre charge. La fragilité ne touche -que `"auto"` — `"user"`, lui, traverse les gestes voisins. +> ⚠️ **La première ligne ne dit PAS ce que j'ai d'abord conclu.** J'y avais lu « l'écho +> verbatim est refusé ». C'est faux : le moteur porte une exception écrite exprès +> (`reconcileRankOrigin()`) qui **accepte `"auto"` quand le persisté le porte déjà** — +> précisément pour ne pas rompre la neutralité `Get` → `Set`. Entre ma lecture et mon +> écriture, la marque avait été consommée par une manipulation concurrente : mon écho était +> donc **périmé**, et son refus est le bon comportement. Deux cas distincts, un seul +> symptôme — et c'est exactement pourquoi une mesure sans relecture immédiate ne prouve +> rien (cf. le contrôle de la suppression, qui relit avant de conclure). +> +> **Ce qui reste à mesurer** : un écho *inchangé* sur une marque *fraîche*. Tant que ce +> n'est pas fait, `sansRankOriginAuto()` reste — et le jour où l'écho passe, elle part et la +> marque survit aux gestes qui ne la concernent pas. + +Le reste tient : `"user"` s'écrit, ne déplace pas le rang, et **survit** aux gestes voisins. +L'effacement observé de `"auto"` est, lui, **le fait de l'app** — de son nettoyage, pas d'un +refus de la box. > ⚠️ **Un refus arrive en `status: "success"`.** Le verdict est dans `params.energyError`, > et un contrôle sur `status` seul le prend pour un succès. Le service de l'app teste bien diff --git a/lib/services/load_priority.dart b/lib/services/load_priority.dart index 7345d01..f260a74 100644 --- a/lib/services/load_priority.dart +++ b/lib/services/load_priority.dart @@ -155,11 +155,15 @@ List tiedByPriority(List entries) { /// installation avec une borne neuve deviendrait non modifiable), renvoyer `"auto"` (refus /// systématique), ou effacer une marque informative. /// -/// **Contournement, pas solution.** La demande est portée au moteur : tolérer l'écho -/// *inchangé*, la règle devant porter sur le **changement** et non sur la **présence** — -/// même distinction que l'effacement de `rankOrigin`, qui porte sur « rang différent pour -/// cet id » et non sur « écriture reçue ». Le jour où l'écho passe, cette fonction disparaît -/// et la marque survit aux gestes qui ne la concernent pas. +/// **Contournement EN SURSIS.** Le moteur porte déjà l'exception demandée — +/// `reconcileRankOrigin()` accepte `"auto"` quand le persisté le porte déjà, pour ne pas +/// rompre la neutralité `Get` → `Set`. Le refus mesuré le 2026-08-29 côté app portait un +/// écho **périmé** : la marque avait été consommée entre la lecture et l'écriture, et +/// refuser un écho périmé est le bon comportement. +/// +/// Dès que l'écho *inchangé* sur une marque *fraîche* sera vérifié, **cette fonction +/// disparaît** — et la marque survivra alors aux gestes qui ne la concernent pas, ce qui +/// est le comportement voulu. Elle ne reste que le temps de cette vérification. /// /// Le motif général — *un interdit d'écriture doit se prononcer sur l'écho inchangé* — est /// écrit en `docs/UI_data_contract.md` §10, avec le contrôle à faire une fois par règle. diff --git a/tools/rpc/README.md b/tools/rpc/README.md index 030ff78..dabf00c 100644 --- a/tools/rpc/README.md +++ b/tools/rpc/README.md @@ -28,6 +28,33 @@ dart tools/rpc/