docs(spec): LM-104 — supposer conservateur pour annoncer, tenter pour agir
Le principe dégagé par les tests d'ECS-411 puis d'ECS-414 ne vit pas sous ECS-414 : il vaut pour tout mécanisme, présent et futur. Il est donc inscrit en LM-104 (spec_loadmodel.md §1), ECS-411 et ECS-414 n'en étant que deux applications. Les deux moitiés sont indissociables, et c'est l'omission de la seconde qui piège : supposer « contact fermé » puis en déduire « donc rien à écrire » transforme une hypothèse de prudence en masquage de panne. La prudence porte sur ce qu'on DIT de l'installation, jamais sur ce qu'on lui ENVOIE. Consigne aussi : le tableau ECS-110 (les deux Q_ASSERT de sgreadyadapter sont supprimés, l'échéance prospective est tombée), la charge utile sg-ready réellement implémentée en LM-300, et le statut de spec_loadmodel.md — §3 n'est plus une intention. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
25cde160ba
commit
4a55d440df
@ -464,6 +464,12 @@ attendre un quart d'heure pour sortir d'un état non voulu n'est pas défendable
|
||||
> Ce lot porte sa propre précondition — retirer `Q_ASSERT(m_stateRelays.contains(2))`
|
||||
> (`sgreadyadapter.cpp:36`) au profit d'un refus explicite, cf. ECS-110.
|
||||
|
||||
> **Le principe dégagé par ces deux exigences ne vit pas ici.** « Supposer
|
||||
> conservateur pour annoncer, tenter systématiquement pour agir » vaut pour tout
|
||||
> mécanisme, pas pour l'ECS : il est inscrit en **LM-104**
|
||||
> (`specs/spec_loadmodel.md` §1). ECS-411 et ECS-414 en sont deux applications,
|
||||
> pas la source.
|
||||
|
||||
**ECS-413 — Désactivation en état sûr.** Désactiver une charge (`enabled: false`)
|
||||
ou la retirer de la configuration DOIT laisser son matériel dans l'**état sûr
|
||||
défini par son adaptateur**, et non dans son dernier état commandé. L'état sûr est
|
||||
@ -530,15 +536,21 @@ Quatre `Q_ASSERT` d'invariant de configuration existent aujourd'hui dans `etm/`
|
||||
|---|---|---|
|
||||
| `relayrouter.cpp:66` | `m_levels` non vide, `[0] == 0` | **Non** — le masque vide garantit la clé 0 par construction |
|
||||
| `etmvariableloadadapter.cpp:34` | `powerLevels[0] == 0` | **Non** — déjà refusé par `isValid()` (`loadconfig.cpp:114-115`) |
|
||||
| `sgreadyadapter.cpp:35` | `states` non vide | **Non** — construction codée en dur |
|
||||
| `sgreadyadapter.cpp:36` | état 2 présent = **repli sûr obligatoire** | **Non** aujourd'hui — construction codée en dur (`energypluginnymea.cpp:66-76`) |
|
||||
| ~~`sgreadyadapter.cpp:35`~~ | `states` non vide | **SUPPRIMÉ (2026-08-09)** — remplacé par le refus `isValid()` |
|
||||
| ~~`sgreadyadapter.cpp:36`~~ | état 2 présent = **repli sûr obligatoire** | **SUPPRIMÉ (2026-08-09)** — voir ci-dessous |
|
||||
|
||||
Aucun ne masque donc de défaut à ce jour : ECS-110 est bien tenu par
|
||||
`LoadConfig::isValid()`, pas par ces assertions. **Le risque est prospectif et il
|
||||
est daté** : le jour où `SgReadyAdapter` bascule sur la configuration — travail
|
||||
déjà inscrit au `DÉFÉRÉ` d'`AGENTS.md` — `sgreadyadapter.cpp:36` devient le seul
|
||||
garde-fou sur la présence de l'état 2, l'état de repli sûr du mode dégradé L2,
|
||||
et ce garde-fou sera absent du binaire livré.
|
||||
`LoadConfig::isValid()`, pas par ces assertions. **Le risque était prospectif et daté ; l'échéance est
|
||||
tombée le 2026-08-09**, quand `SgReadyAdapter` est passé à la configuration. Les
|
||||
deux assertions ont donc été retirées **avant** ce basculement, dans cet ordre,
|
||||
et remplacées par deux gardes réels :
|
||||
|
||||
- `LoadConfig::isValid()` **refuse** toute configuration `sg-ready` dépourvue de
|
||||
l'état 2, avec un message explicite. Une PAC inapte au repli sûr du mode
|
||||
dégradé L2 n'est pas construite ;
|
||||
- `SgReadyAdapter` conserve un drapeau `m_usable` : si on le construit malgré
|
||||
tout hors du chemin de configuration, il **journalise en critique et refuse
|
||||
toute commande** au lieu de disparaître sous `QT_NO_DEBUG`.
|
||||
|
||||
Exigence : toute vérification portant sur des données venant de la configuration
|
||||
DOIT vivre dans `isValid()` ou dans la construction de l'adaptateur, avec un
|
||||
@ -628,6 +640,7 @@ mécanique reste **L0**. Perte de sonde → mode sans sonde, pas arrêt d'urgenc
|
||||
| ECS-411 | simulation | `testEcsRestartRecovery` |
|
||||
| ECS-413 | simulation | `testEcsDisableLeavesSafeState` — + cas négatif : un rebuild sans désactivation ne coupe pas |
|
||||
| ECS-414 | simulation | `testSgReadyPartialFailure` — plancher = état 2, atomicité du repli, contact injoignable |
|
||||
| ECS-110 (SG-Ready) / LM-300 | simulation | `testSgReadyFromConfig` — refus sans état 2, refus de charges utiles mélangées, round-trip, pilotage, désactivation → état 2 |
|
||||
| ECS-110, ECS-111 | unitaire | `testEcsConfigValidation` — DOIT s'exécuter aussi en build release (`QT_NO_DEBUG`), sinon il ne prouve rien du binaire livré |
|
||||
| ECS-501, ECS-502 | simulation | `testEcsStageFault` |
|
||||
| ECS-601, ECS-602 | simulation | `testEcsTemperatureTarget` |
|
||||
@ -698,6 +711,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-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 |
|
||||
| 2026-08-09 | **ECS-414 FAIT** — échelle généralisée à SgReady et EtmVariableLoad ; plancher = état 2 pour la PAC ; repli soumis au même `transientHarm` que l'aller ; contact injoignable supposé fermé mais toujours commandé |
|
||||
| 2026-08-09 | **ECS-414 créé** — généralisation d'ECS-410 à SgReady et EtmVariableLoad, rattachée au lot SG-Ready en configuration |
|
||||
| 2026-08-09 | **ECS-413 créé** — `enabled: false` laissait les relais fermés (constat de banc, juste avant une intervention de câblage). État sûr propre à chaque adaptateur ; ne s'applique PAS à un redémarrage de `nymead`, qu'ECS-411 couvre |
|
||||
|
||||
@ -1,8 +1,16 @@
|
||||
# SPEC — Modèle de charges (domaine × mécanisme)
|
||||
|
||||
Version : 0.1.1
|
||||
Version : 0.1.2
|
||||
Dépôt : `etm-powersync-energy-plugin-etm`
|
||||
Statut : **intention de conception figée. Aucune implémentation immédiate.**
|
||||
Statut : **intention de conception figée.** §3 (schéma persisté) est **en cours
|
||||
d'implémentation** depuis le 2026-08-09 : `relay-router`, `etmvariableload` et
|
||||
`sg-ready` sont trois charges utiles discriminées par `adapter`. Le reste du
|
||||
document reste une intention.
|
||||
|
||||
> **0.1.2** — LM-104 ajouté (§1) : « supposer conservateur pour annoncer, tenter
|
||||
> systématiquement pour agir ». Principe dégagé des tests d'ECS-411 et ECS-414,
|
||||
> inscrit ici parce qu'il vaut pour tout mécanisme et non pour l'ECS seul.
|
||||
> §3 : le mécanisme `sg-ready` a sa charge utile et cesse d'être une intention.
|
||||
|
||||
> **0.1.1** — passe de vérification contre le code. Quatre précisions :
|
||||
> préséance des verrous (LM-303), portée de l'union discriminée (LM-302),
|
||||
@ -35,6 +43,27 @@ domaine ne détermine pas le mécanisme et réciproquement.
|
||||
retire de l'allocation via `available` ; elle ne la négocie pas. Règles absolues
|
||||
1 et 2 d'`AGENTS.md` inchangées.
|
||||
|
||||
**LM-104 — Supposer conservateur pour annoncer, tenter systématiquement pour agir.**
|
||||
Un adaptateur qui ne peut pas LIRE l'état d'un organe DOIT le supposer dans l'état
|
||||
le plus consommateur (contact fermé, palier engagé) quand il **annonce** — dans
|
||||
`telemetry()`, `toLoadContext()` ou la déduction de son état courant au démarrage.
|
||||
Le même adaptateur DOIT néanmoins **émettre l'écriture** vers cet organe quand il
|
||||
agit, sans jamais la court-circuiter au motif que l'état supposé coïncide déjà avec
|
||||
la cible.
|
||||
|
||||
Les deux moitiés sont indissociables, et c'est l'omission de la seconde qui est
|
||||
piégeuse : supposer « fermé » puis en déduire « donc rien à écrire » transforme une
|
||||
hypothèse de prudence en **masquage de panne** — l'organe reste dans son état réel,
|
||||
inconnu, et aucun verdict d'échec ne se déclenche. La prudence porte sur ce qu'on
|
||||
DIT de l'installation, jamais sur ce qu'on lui ENVOIE.
|
||||
|
||||
Portée : tout mécanisme, présent et futur. `Relay` (déduction de palier au
|
||||
démarrage), `SgReady` (relevé du motif de contacts réellement fermés), et par
|
||||
construction `ModbusSetpoint` dès qu'un registre devient illisible. Origine :
|
||||
deux défauts symétriques trouvés par les tests d'ECS-411 puis d'ECS-414 —
|
||||
d'abord l'hypothèse inverse (« injoignable donc ouvert »), puis son corollaire
|
||||
(« supposé fermé donc jamais écrit »).
|
||||
|
||||
**Justification.** Un nommage par domaine seul (`EcsAdapter`, `HvacAdapter`)
|
||||
obligerait à écrire l'encodage SG-Ready deux fois (ECS et HVAC), la combinatoire
|
||||
de relais deux fois (ECS et EV en prise commandée), et le transport Modbus deux
|
||||
@ -75,8 +104,30 @@ LoadConfig {
|
||||
}
|
||||
```
|
||||
|
||||
**État d'implémentation (2026-08-09).** Trois charges utiles existent, discriminées
|
||||
par le champ `adapter` du schéma persisté — `relay-router`, `etmvariableload`,
|
||||
`sg-ready`. Le domaine, le groupe et les facettes ne sont pas encore portés : ce
|
||||
qui est figé, c'est la **forme**, celle qui coûte cher à rattraper.
|
||||
|
||||
```
|
||||
adapter = "sg-ready" → sgReady {
|
||||
states[] { state : 1..4, relays[] : thingId, estimatedPowerW }
|
||||
minStateHoldS
|
||||
}
|
||||
```
|
||||
|
||||
`estimatedPowerW` porte volontairement ce nom : c'est une **estimation**, jamais un
|
||||
engagement — une PAC ne consomme pas la même chose à −5 °C et à +12 °C. Le champ
|
||||
sert à ordonner et à budgéter, la mesure reste la source de vérité (ECS-500).
|
||||
L'état 2 est **obligatoire** : c'est le plancher de repli du mode dégradé L2 et de
|
||||
la désactivation (ECS-413/414). Une configuration qui ne peut pas l'exprimer est
|
||||
refusée, pas construite.
|
||||
|
||||
**LM-301** — Ajouter un mécanisme DOIT se limiter à un type de charge utile et
|
||||
une branche de fabrique. Rien d'autre ne bouge.
|
||||
une branche de fabrique. Rien d'autre ne bouge. **Vérifié à l'usage** : l'ajout de
|
||||
`sg-ready` a touché exactement une charge utile, une branche de `isValid()`, une
|
||||
branche de fabrique dans `rebuildLoadAdapters()`, et un champ optionnel au schéma
|
||||
RPC. Aucun code d'arbitrage n'a bougé.
|
||||
|
||||
**LM-302** — Chaque charge utile valide la sienne. Les états invalides doivent
|
||||
être inexprimables : pas de registre Modbus dans une configuration relais.
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user