docs+deb: ECS-309, renvoi ECS-306, ECS-412 cold-start, LM-302 réécrit — 1.15.2+etm10
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
2e6aa65092
commit
b87bfa6b6d
@ -1,3 +1,20 @@
|
||||
powersync-energy-plugin-nymea (1.15.2+etm10) trixie; urgency=medium
|
||||
|
||||
* ECS-309 — motif de décision fidèle au mécanisme. Lorsqu'un verrou, et non le budget,
|
||||
produit le palier appliqué, le decisionReason nomme le verrou et la contrainte qu'il
|
||||
impose. Le chemin minOff — celui de l'armement à froid d'ECS-412 — publiait
|
||||
« Surplus insuffisant (7706 W) » alors que 7706 W étaient précisément disponibles :
|
||||
la décision était juste, le motif accusait le budget. Il cite maintenant le budget
|
||||
DISPONIBLE, pour couper court à l'explication budgétaire.
|
||||
* Les trois causes sont distinguables à la lecture : budget réellement insuffisant,
|
||||
verrou minOn maintenant un palier au-dessus du budget, verrou minOff empêchant
|
||||
l'enclenchement. Les cas minOn et minStateHold (PAC) étaient déjà fidèles.
|
||||
* La règle 7 d'AGENTS.md exigeait un motif non vide ; ECS-309 l'étend de la présence à
|
||||
la fidélité, et exige que les tests portent sur le mécanisme annoncé plutôt que sur
|
||||
la non-vacuité de la chaîne — un test « motif non vide » n'aurait rien vu.
|
||||
|
||||
-- Patrick Schurig <etm.schurig@gmail.com> Tue, 11 Aug 2026 10:00:00 +0200
|
||||
|
||||
powersync-energy-plugin-nymea (1.15.2+etm9) trixie; urgency=medium
|
||||
|
||||
* Corrige le va-et-vient JSON-RPC cassé par l'union LM-300. GetLoadConfig sérialise
|
||||
|
||||
@ -229,6 +229,34 @@ correction DEVRAIT restaurer un signal de verrou vers le scheduler plutôt que s
|
||||
contenter de remonter le palier appliqué après coup : la première corrige dans le
|
||||
cycle, la seconde seulement au cycle suivant.
|
||||
|
||||
Le motif publié lorsque le clamp mord est régi par **ECS-309**.
|
||||
|
||||
**ECS-309 — Motif de décision fidèle au mécanisme.** Lorsque le palier appliqué
|
||||
résulte d'un **verrou** et non du budget, le `decisionReason` DOIT nommer le verrou
|
||||
et la contrainte qu'il impose. Il NE DOIT PAS attribuer l'issue au budget.
|
||||
|
||||
`AGENTS.md` règle 7 exige un motif non vide sur chaque action ; cette exigence
|
||||
l'étend de la **présence** à la **fidélité**. Un motif faux est pire qu'un motif
|
||||
absent : il envoie diagnostiquer le mauvais problème, et il le fait avec l'autorité
|
||||
d'une explication.
|
||||
|
||||
Trois cas DOIVENT être distinguables à la lecture du texte :
|
||||
|
||||
| Cause réelle | Ce que le motif doit dire |
|
||||
|---|---|
|
||||
| budget réellement insuffisant | le budget, et son montant |
|
||||
| verrou `minOn` maintenant un palier **supérieur** au budget | le verrou `minOn`, et le palier qu'il retient |
|
||||
| verrou `minOff` empêchant l'enclenchement | le verrou `minOff`, et l'interdiction de monter |
|
||||
|
||||
**Cas observé au banc (2026-08-09)**, pendant la fenêtre d'armement à froid
|
||||
d'ECS-412 : palier plafonné à 0 avec **7706 W de surplus disponible**, et motif
|
||||
publié « Surplus insuffisant (7706 W) ». La décision était juste, le motif disait
|
||||
l'inverse de la cause.
|
||||
|
||||
**Test.** Les tests de motif DOIVENT porter sur le **mécanisme annoncé**, pas sur
|
||||
la non-vacuité de la chaîne. Un test qui vérifie « motif non vide » ne distingue
|
||||
aucun des trois cas ci-dessus et n'aurait rien vu du défaut de banc.
|
||||
|
||||
**ECS-412 — Survie des horodatages de verrou au `rebuild`.**
|
||||
`rebuildLoadAdapters()` (`energyarbitrator.cpp:137-144`) détruit et reconstruit
|
||||
les adaptateurs à chaque `SetLoadConfig`, remettant `m_currentStage` **et**
|
||||
@ -264,6 +292,10 @@ peut jamais s'enclencher, donc jamais commuter, donc jamais valider son
|
||||
horodatage. Passer `now` au constructeur n'est PAS la réponse : cela changerait le
|
||||
contrat d'`ILoadAdapter` pour un cas particulier.
|
||||
|
||||
Pendant la fenêtre d'armement à froid, le motif publié DOIT nommer l'armement
|
||||
`minOff`, conformément à **ECS-309**. C'est sur ce chemin que le défaut de motif a
|
||||
été observé ; `testEcsColdStartLockExpires` DOIT donc l'asserter.
|
||||
|
||||
**Symétrie exigée.** Les deux cas de démarrage à froid sont armés de la même
|
||||
façon et expirent de la même façon :
|
||||
|
||||
@ -662,6 +694,7 @@ mécanique reste **L0**. Perte de sonde → mode sans sonde, pas arrêt d'urgenc
|
||||
| Exigence | Type | Test |
|
||||
|---|---|---|
|
||||
| ECS-306 | simulation | `testEcsBudgetUnderLock` |
|
||||
| ECS-309 | simulation | `testEcsColdStartLockExpires` — les trois causes distinguées par le TEXTE du motif (minOff / budget / minOn), vérifié échouant sans le correctif |
|
||||
| ECS-412 | simulation | `testEcsRebuildPreservesLock` + **`testEcsColdStartLockExpires`** (armement à froid transitoire) |
|
||||
| ECS-303 | banc | relevé de commutations à la frontière de recombinaison (§4.0) — **mesurer avant de coder** |
|
||||
| ECS-305 | unitaire | `testEcsSwitchCount` — compteur par relais |
|
||||
@ -744,6 +777,7 @@ protocole dans le même lot.**
|
||||
| 2026-08-08 | **Étape 1 OUVERTE** — trois exigences : ECS-306, ECS-411, ECS-412 |
|
||||
| 2026-08-08 | ECS-412 : au démarrage à froid, le verrou est **ARMÉ** (défaut sûr), jamais purgé |
|
||||
| 2026-08-09 | ECS-412 précisé : cet armement est **TRANSITOIRE** et posé paresseusement au premier `now`. Défaut de blocage circulaire constaté **au banc**, corrigé, couvert par `testEcsColdStartLockExpires` |
|
||||
| 2026-08-11 | **ECS-309 créé et FAIT** — motif fidèle au mécanisme. Le chemin minOff attribuait l'issue au budget ; le motif nomme désormais le verrou et cite le budget DISPONIBLE pour couper court à l'explication budgétaire. Les cas minOn et `minStateHold` étaient déjà fidèles |
|
||||
| 2026-08-10 | **Va-et-vient RPC cassé par LM-300** — `GetLoadConfig` sérialise via le méta-objet, donc un `sgReady` VIDE sur toute charge non SG-Ready ; le schéma SET exigeait `states` dès la clé présente, et nymea rejetait avant le handler. Constaté au banc à la première tentative réelle de `SetLoadConfig` : aucun test ne rejouait le geste de l'app (lire, modifier, réécrire). Corrigé en `o:states` — c'est le mur documenté en LM-302, la validation réelle restant dans `isValid()` |
|
||||
| 2026-08-09 | **ECS-110-b créé et FAIT** — un Thing n'appartient qu'à une charge active. Trou trouvé en répondant à la question « le cas général est-il couvert ? » : il ne l'était pas du tout. `LoadConfigStore::validateSet()` ; doublons intra-charge également refusés |
|
||||
| 2026-08-09 | **SG-Ready configurable (LM-300)** — union discriminée par mécanisme ; les deux `Q_ASSERT` de `sgreadyadapter.cpp` retirés au profit d'un refus `isValid()` + drapeau `m_usable` (ECS-110 tenu hors `Q_ASSERT`) ; principe général porté en LM-104, hors ECS |
|
||||
|
||||
@ -136,25 +136,26 @@ peut la vérifier, quelle que soit sa rigueur. Elle vit donc au niveau du store
|
||||
`LoadConfig::claimedThingIds()` — c'est le seul point à étendre, et l'oublier rend le
|
||||
mécanisme invisible à la vérification. Voir ECS-110-b.
|
||||
|
||||
**LM-302** — Chaque charge utile valide la sienne. Les états invalides doivent
|
||||
être inexprimables : pas de registre Modbus dans une configuration relais.
|
||||
**LM-302 — Charges utiles auto-validantes, et portée de l'« inexprimable ».**
|
||||
Chaque charge utile valide la sienne. Les états invalides doivent être inexprimables
|
||||
dans les **types C++** et le **schéma persisté** ; à la frontière **JSON-RPC**, la
|
||||
validation est **à l'exécution**, par contrainte nymea.
|
||||
|
||||
**Portée exacte.** L'union discriminée s'applique aux **types C++** et au
|
||||
**schéma persisté**. Elle ne s'applique **pas** au schéma JSON-RPC : nymea valide
|
||||
les paramètres *avant* le handler, si bien que deux formes exclusives obligent à
|
||||
marquer tous les champs spécifiques en optionnel (`"o:"`), faute de quoi l'une
|
||||
des deux serait rejetée en amont. La contrainte est déjà documentée dans
|
||||
`nymeaenergyjsonhandler.cpp:155-160` pour deux mécanismes ; elle s'aggrave à
|
||||
quatre. **Le mur a été touché le 2026-08-10**, exactement où ce paragraphe l'annonçait, et d'une
|
||||
façon qu'il n'avait pas anticipée : ce n'est pas seulement que deux formes exclusives
|
||||
obligent à marquer les champs `"o:"`, c'est que `pack<LoadConfig>()` sérialise **toutes**
|
||||
les propriétés déclarées — une charge `relay-router` sort donc de `GetLoadConfig` avec un
|
||||
`sgReady` vide. Exiger une clé **à l'intérieur** d'une charge utile optionnelle casse
|
||||
alors le va-et-vient de l'app sur les charges des **autres** mécanismes. Corollaire pour
|
||||
tout mécanisme futur : les clés internes d'une charge utile se marquent `"o:"` elles
|
||||
aussi. À la frontière RPC, la validation reste donc **à l'exécution**
|
||||
(`LoadConfig::isValid()`). Ne pas tenter un schéma RPC strict : c'est un mur
|
||||
connu.
|
||||
**Corollaire vérifié à l'usage (2026-08-09).** `GetLoadConfig` sérialise via le
|
||||
méta-objet, donc **toutes** les propriétés déclarées apparaissent dans la sortie — y
|
||||
compris la charge utile **vide** d'un autre mécanisme. Un schéma qui exige une clé
|
||||
interne dès que la charge utile est présente rend donc impossible l'aller-retour
|
||||
lire-puis-réécrire, c'est-à-dire le geste ordinaire de l'application, et le rejet
|
||||
survient **avant** le handler.
|
||||
|
||||
**Règle.** Toutes les clés internes d'une charge utile optionnelle DOIVENT être
|
||||
déclarées optionnelles (`o:`) au schéma RPC. L'exigence réelle vit dans `isValid()`,
|
||||
jamais dans le schéma. Les clés internes d'une charge utile optionnelle contraignent
|
||||
les charges des **autres** mécanismes — c'est le côté par lequel le mur se touche.
|
||||
|
||||
**Obligation permanente.** Tout mécanisme ajouté DOIT être accompagné d'un test
|
||||
d'aller-retour `GetLoadConfig` → `SetLoadConfig` **inchangé**, vérifié échouant sans
|
||||
le correctif. Sans cette obligation, le prochain mécanisme rejouera la scène.
|
||||
|
||||
**LM-303 — Verrous.** `typeAppareil` fournit des **défauts** (résistif ≈ 60 s,
|
||||
thermodynamique ≈ 300 s), l'installateur peut les modifier, et c'est la valeur
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user