diff --git a/AGENTS.md b/AGENTS.md index 86d03e2..ae7fad3 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1154,6 +1154,30 @@ aucune, la décision est une intention, et elle se périmera à la vitesse du co > cran plus fin que « vérifier le test échouant » : **on peut voir un test échouer pour la > mauvaise raison, et on peut le voir passer pour une raison qui n'est pas la sienne.** +> ### Et le cas INVERSE : une contre-épreuve inerte qui n'est PAS un défaut (2026-09-06) +> +> Tout ce qui précède traite l'inertie comme un signal d'alarme. Il existe un cas où elle est +> **saine**, et il faut savoir le distinguer — sinon on ouvre une chasse au défaut sur du code +> correct, ce qui coûte deux fois : le temps, et la confiance dans le signal. +> +> **Le cas** : le mécanisme tire sa valeur du CONTEXTE, pas de la valeur qu'on perturbe. Perturber +> le champ ne peut alors rien changer, non pas parce que le chemin est mort, mais parce que le +> champ n'est pas ce qui décide. Relevé sur le minimum réseau : le plancher vient de la borne +> (`6 A × phases × tension`), pas du champ déclaré — donc modifier le champ laisse le plancher +> intact, et c'est correct. +> +> **Le test qui sépare les deux**, et c'est le même que partout : *qu'est-ce qui déciderait, si ce +> n'est pas ça ?* +> +> - **Inertie MALSAINE** : on ne sait pas nommer ce qui décide à la place → le chemin est +> probablement mort, ou l'assertion est garantie par un invariant qu'on n'a pas identifié. +> - **Inertie SAINE** : on nomme précisément l'autre source, et on peut la perturber ELLE pour +> voir la contre-épreuve mordre. +> +> **La conséquence pratique** : une inertie saine n'est acceptable que si la contre-épreuve est +> **déplacée sur la vraie source**, jamais abandonnée. Écrire « ne mord pas, c'est normal » sans +> exhiber le contrôle qui mord, c'est refermer l'identité sur elle-même une fois de plus. + > **Un troisième mode d'érosion, et il est le SYMÉTRIQUE des deux autres (2026-08-31).** Le > garde-fou dit « une règle que rien ne rejoue se périme ». Voici l'inverse : **une règle rendue > IMMORTELLE par sa propre garde.** diff --git a/INTERFACE.md b/INTERFACE.md index 20ff59e..ee54de4 100644 --- a/INTERFACE.md +++ b/INTERFACE.md @@ -1350,6 +1350,29 @@ se distingue pas d'une panne. > > Même paire, même raison que `progress.measuredSince` / `targetWh` de l'avancement éco. +> #### ⚠️ DÉFAUT CONNU — `measuredSince` DÉRIVE au changement de fuseau (2026-09-06) +> +> **Mesuré, non corrigé à ce jour.** Bascule `Europe/Paris` → `Europe/London` à `13:37:26Z`, +> sans redémarrage de `nymead` : +> +> ``` +> periodStartedAt : 1788645600 → 1788649200 (+3600 s) ← CORRECT, la journée suit le fuseau +> measuredSince : 1788645607 → 1788649207 (+3600 s) ← FAUX, un instant absolu ne bouge pas +> ``` +> +> **Cause** : `m_baselinePosee = now` conserve le `QDateTime` tel quel, en spécification +> `Qt::LocalTime`. Un tel objet garde des composantes d'horloge murale et résout son époque +> **au moment de la conversion**, avec le fuseau courant. Après une bascule, la même lecture +> « 00:00:07 » rend une époque différente d'une heure. +> +> **Conséquence** : le champ affirme que la baseline a été posée une heure plus tard qu'en +> réalité. Le champ posé pour rendre une couture lisible **est lui-même déplacé par la couture**. +> +> **Correctif** : `m_baselinePosee = now.toUTC()` — une ligne. Un instant absolu se stocke en +> UTC ; `periodStartedAt`, lui, doit rester construit dans le fuseau courant, puisque c'est +> précisément une frontière locale. Les deux champs n'ont pas la même nature, et le stockage doit +> le refléter. + > #### Ce que `timeZone` rend possible, et qui n'existait pas > > La période de reporting est la **troisième nature d'heure murale** du système, après l'habitude diff --git a/docs/DESIGN_MIN_RESEAU.md b/docs/DESIGN_MIN_RESEAU.md index 7603961..b3575f7 100644 --- a/docs/DESIGN_MIN_RESEAU.md +++ b/docs/DESIGN_MIN_RESEAU.md @@ -212,3 +212,21 @@ ne peut pas exercer le cas mixte, celui où le minimum complète un surplus part Il l'exercera en revanche très bien sur une charge en watts — le chauffe-eau — dès qu'elle aura un compteur réel (`TEST_TERRAIN.md` §12). **C'est la même précondition que pour l'étape 4 de la commande perdue**, et elle est maintenant nommée par un quatrième chemin. + +## 8. Ce lot ne sera PAS vérifiable sur machine — écrit avant, pas découvert en mesurant + +La voiture a été débranchée le 2026-09-06. `pluggedIn` retombe, la borne **sort de `loads[]`** +(vérifié : `['chauffe-eau', 'pac-terrain']`), et rien ne la commande plus. + +**Donc `EV_MIN_GRID` tiendra sur ses tests unitaires et sa contre-épreuve, pas sur un relevé.** +C'est acceptable *à condition de le dire maintenant* : le découvrir au moment de mesurer ferait +prendre une absence de mesure pour une absence de défaut. + +Ce qu'il faudra pour lever la réserve, et c'est cumulatif : + +1. une voiture branchée — trivial, mais elle décide de tout le reste ; +2. un lien V2C sain — trois semaines que c'est le blocage (`PORTING_STATUS.md`) ; +3. et pour le **cas mixte** — minimum complété par un surplus partiel — une charge en watts avec + un compteur réel, parce que 2 kWc contre un plancher de 4 140 W ne le produira jamais. + +Les trois sont indépendantes, et aucune n'est à notre main aujourd'hui.