docs(spec): spec_ecs 0.5.0 — §13-1/§13-2 tranchés, étape 1 ouverte, câblage confirmé
Précondition de la consigne d'étape 1 : les deux arbitrages étaient RELEVÉS au
journal, pas TRANCHÉS. Ils le sont maintenant, datés, dans les termes reçus.
§13-1 CLOS — le schéma « ARCHITECTURE CIBLE » d'AGENTS.md a été redessiné par le
lot de consolidation (f6a4be5) : RelayRouter et EtmVariableLoadAdapter y
figurent, le kind Stage en est retiré, SocketScheduler et BatteryAdapter sont
marqués non écrits.
§13-2 CLOS, lecture (b) — ECS-412 réduit à m_lastSwitch et redirigé vers sa cause
racine (rebuild incrémental : ne reconstruire que ce qui a changé) ; ECS-411
remonté en étape 1. L'étape 1 porte donc TROIS exigences : ECS-306, ECS-411,
ECS-412. Ajouté explicitement : au démarrage à froid, m_lastSwitch étant
irrécupérable, le verrou DOIT être ARMÉ et non purgé — l'implémentation naturelle
(QDateTime nul = verrou inactif) fait l'inverse et court-circuiterait la
protection compresseur sur une boucle de redémarrage.
Câblage du banc confirmé 500/1000/2000 (pondération binaire, 8 paliers de 0 à
3500 W au pas de 500, aucune redondance). Trois conséquences consignées :
- ECS-302 n'est PAS exercé par ce matériel — un seul encodage par palier. La
restructuration de m_relayMapping perd sa justification immédiate et cesse
d'être un préalable structurel de l'étape 3.
- ECS-305 monte en importance : sur un balayage complet R500 commute 7 fois,
R1000 3 fois, R2000 une seule. C'est la seule mesure d'usure utile.
- ECS-303 CRÉÉ — point dur 1500→2000 W : les trois relais basculent d'un coup,
donc trou de puissance à chaque franchissement avec l'ordre
coupure-avant-fermeture. La parade est un seuil d'hystérésis élargi à cette
frontière, pas un réordonnancement. À MESURER au banc avant de coder.
Aucun code touché.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
41d3a329e4
commit
1e80a83df9
@ -1,6 +1,6 @@
|
||||
# SPEC — ECS multi-palier
|
||||
|
||||
Version : 0.4.3
|
||||
Version : 0.5.0
|
||||
Dépôt : `etm-powersync-energy-plugin-etm` (experience-plugin ems)
|
||||
Branche : `feature/beta-rulebased`
|
||||
Contrats faisant autorité : `docs/OPTIMIZER_PROTOCOL.md`, `docs/SAFETY.md`, `AGENTS.md`
|
||||
@ -30,6 +30,11 @@ remonte. Il ne tranche pas seul.
|
||||
>
|
||||
> **0.4.3** — ECS-110 étendu : la validation ne doit pas reposer sur `Q_ASSERT`,
|
||||
> absent du binaire release. Constat issu de la consolidation d'`AGENTS.md`.
|
||||
>
|
||||
> **0.5.0** — §13-1 et §13-2 **tranchés et consignés** ; l'étape 1 est débloquée.
|
||||
> ECS-412 réduit à `m_lastSwitch` (lecture (b)) et redirigé vers sa cause racine.
|
||||
> Câblage du banc confirmé **500/1000/2000** : ECS-302 n'est plus exercé par ce
|
||||
> matériel, ECS-305 monte, ECS-303 créé pour le point dur 1500→2000 W.
|
||||
|
||||
---
|
||||
|
||||
@ -140,9 +145,10 @@ L'ordre est normatif : chaque étape conditionne la suivante.
|
||||
défauts qui produisent un mauvais comportement sur du code qui tourne au banc,
|
||||
pas des fonctionnalités manquantes. Ils passent devant tout le reste.
|
||||
|
||||
> Périmètre d'ECS-412 en étape 1 : **suspendu à l'arbitrage §13-2**, qui peut le
|
||||
> réduire à `m_lastSwitch` et remonter ECS-411 ici. Ne pas ouvrir l'étape 1 sans
|
||||
> l'avoir tranché.
|
||||
> **Tranché le 2026-08-08 (§13-2, lecture (b)) — l'étape 1 est OUVERTE.** Elle
|
||||
> porte **trois** exigences : ECS-306, **ECS-411** (remonté ici) et ECS-412
|
||||
> **réduit à `m_lastSwitch`**. `m_currentStage` se redéduit des Things une fois
|
||||
> ECS-411 en place : il ne demande aucun code dédié.
|
||||
|
||||
**Étape 2 — extraction du noyau de calcul (type pur, sans `ThingManager`).**
|
||||
Sortir `m_levels`, `stageForPower` et `lockWindow`. Débloque ECS-304 en vrai
|
||||
@ -156,8 +162,10 @@ unitaire et conditionne l'étape 3.
|
||||
**Étape 3 — ECS-302 / ECS-305.** Changement de structure de `m_relayMapping`.
|
||||
Doit précéder tout ce qui s'appuiera dessus.
|
||||
|
||||
**Étape 4 — ECS-411 puis ECS-410**, même chemin `applyRelayStage` / `available`,
|
||||
à faire ensemble. ECS-411 peut remonter en étape 1 selon l'arbitrage §13-2.
|
||||
**Étape 4 — ECS-410** (échec d'écriture). ECS-411 a été **remonté en étape 1**
|
||||
(§13-2). ECS-410 reste ici : il touche le même chemin `applyRelayStage` /
|
||||
`available`, mais il demande d'attendre le résultat de `executeAction`, ce qui
|
||||
est un changement de nature — pas une lecture d'état au démarrage.
|
||||
|
||||
**Étape 5 — ECS-110 (complément) et ECS-111.**
|
||||
|
||||
@ -197,11 +205,31 @@ 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.
|
||||
|
||||
**ECS-412 — Survie au `rebuild`.** `rebuildLoadAdapters()`
|
||||
(`energyarbitrator.cpp:137-144`) détruit et reconstruit les adaptateurs à chaque
|
||||
`SetLoadConfig`, remettant `m_currentStage` **et** `m_lastSwitch` à zéro. Le
|
||||
palier courant et les horodatages de verrou DOIVENT survivre à un `rebuild`
|
||||
lorsque la charge concernée n'a pas changé de câblage.
|
||||
**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**
|
||||
`m_lastSwitch` à zéro.
|
||||
|
||||
**Périmètre réduit à `m_lastSwitch`** (§13-2, lecture (b)) : `m_currentStage` se
|
||||
redéduit de l'état réel des Things une fois ECS-411 en place, donc il ne demande
|
||||
aucun code dédié ici. `m_lastSwitch` est l'irréductible — l'horodatage d'un
|
||||
verrou n'existe nulle part dans le matériel — et c'est la part qui protège le
|
||||
compresseur.
|
||||
|
||||
**Corriger la cause racine, pas le symptôme.** Ne PAS transporter `m_lastSwitch`
|
||||
à travers la reconstruction : `rebuildLoadAdapters()` DOIT ne reconstruire **que
|
||||
ce qui a changé**. Une charge dont le câblage et les verrous sont identiques ne
|
||||
doit pas être détruite parce qu'une AUTRE charge a changé de priorité. Cela règle
|
||||
`m_lastSwitch`, évite des réécritures de relais inutiles, et vaut pour toutes les
|
||||
charges — y compris `EtmVariableLoadAdapter`.
|
||||
|
||||
**Démarrage à froid.** Après un redémarrage de `nymead`, `m_lastSwitch` est
|
||||
irrécupérable. Le défaut sûr est alors d'**ARMER** le verrou — traiter la
|
||||
dernière commutation comme venant d'avoir lieu — et non de le purger. Une boucle
|
||||
de redémarrage court-circuiterait sinon la protection compresseur exactement
|
||||
quand elle est la plus nécessaire. **L'implémentation naturelle fait l'inverse
|
||||
(`QDateTime` nul = verrou inactif) : ce point DOIT être écrit explicitement dans
|
||||
le code et couvert par un test.**
|
||||
|
||||
Il ne s'agit pas de confort : les verrous sont de la **protection matérielle**.
|
||||
Avec un `minOn` de 300 à 600 s sur un ballon thermodynamique, un client qui
|
||||
@ -222,7 +250,44 @@ fenêtre de verrou active ».
|
||||
|
||||
## §4 — Combinatoire (`RelayRouter`)
|
||||
|
||||
**ECS-302 — Encodages équivalents.** Un palier DOIT pouvoir porter **plusieurs**
|
||||
> **Câblage du banc CONFIRMÉ le 2026-08-08 : 500 / 1000 / 2000 W.** Pondération
|
||||
> binaire — huit valeurs distinctes de 0 à 3500 W au pas de 500 W, **aucune
|
||||
> redondance**, un seul encodage par palier.
|
||||
>
|
||||
> | Palier | Relais ON | W |
|
||||
> |---|---|---|
|
||||
> | 0 | — | 0 |
|
||||
> | 1 | 500 | 500 |
|
||||
> | 2 | 1000 | 1000 |
|
||||
> | 3 | 500+1000 | 1500 |
|
||||
> | 4 | 2000 | 2000 |
|
||||
> | 5 | 500+2000 | 2500 |
|
||||
> | 6 | 1000+2000 | 3000 |
|
||||
> | 7 | 500+1000+2000 | 3500 |
|
||||
>
|
||||
> Ceci clôt le point ouvert « 500/1000/1500 ou 500/1000/2000 » et déplace trois
|
||||
> exigences — voir ECS-302, ECS-305 et ECS-303 ci-dessous.
|
||||
|
||||
**ECS-302 — Encodages équivalents. NON EXERCÉ PAR CE MATÉRIEL.** Avec une
|
||||
pondération binaire il n'y a **rien à arbitrer** : un seul encodage par palier.
|
||||
L'exigence reste valable pour un câblage 1-2-3 ailleurs (deux encodages pour
|
||||
1500 W), mais **le changement de structure de `m_relayMapping` perd sa
|
||||
justification immédiate** : il redescend dans l'ordre de l'étape 3 et **cesse
|
||||
d'être un préalable structurel**. Ne plus le traiter comme tel.
|
||||
|
||||
**ECS-303 — Point dur 1500 → 2000 W.** Cette transition bascule **les trois
|
||||
relais d'un coup** (R500 et R1000 s'ouvrent, R2000 se ferme). Avec l'ordre
|
||||
coupure-avant-fermeture d'ECS-402, elle produit un **trou de puissance à chaque
|
||||
franchissement**, en plein milieu de la plage de surplus utile.
|
||||
|
||||
La parade n'est PAS dans la combinatoire — aucun réordonnancement ne supprime le
|
||||
trou, puisque aucun sous-ensemble intermédiaire ne vaut ni 1500 ni 2000 W. Elle
|
||||
est dans un **seuil d'hystérésis élargi à cette frontière précise**. Entrée
|
||||
commune avec ECS-605 (hystérésis paramétrable par charge). **À MESURER au banc
|
||||
avant de coder quoi que ce soit** : c'est le relevé de commutations qui dira
|
||||
quelle largeur donner au seuil.
|
||||
|
||||
**ECS-302 (suite) — Encodages équivalents.** Un palier DOIT pouvoir porter **plusieurs**
|
||||
combinaisons de relais, et la combinaison retenue DOIT être celle dont le coût de
|
||||
transition depuis l'état courant est le plus faible : d'abord le nombre de relais
|
||||
à basculer, puis le relais dont le compteur de commutations est le moins entamé.
|
||||
@ -236,10 +301,15 @@ rencontrée gagne, c'est-à-dire le masque le plus bas. Sur un câblage
|
||||
porter deux combinaisons. ECS-302 est donc un changement de structure, pas
|
||||
l'ajout d'un critère.
|
||||
|
||||
**ECS-305 — Compteurs de commutations.** Chaque relais DOIT porter un compteur
|
||||
de commutations, exposé en télémétrie. Le relais de plus faible puissance est le
|
||||
bit de poids faible de la combinatoire et s'usera le premier ; c'est la grandeur
|
||||
qui décide de la durée de vie des contacteurs.
|
||||
**ECS-305 — Compteurs de commutations. IMPORTANCE RELEVÉE** par le câblage
|
||||
binaire confirmé. Chaque relais DOIT porter un compteur de commutations, exposé
|
||||
en télémétrie.
|
||||
|
||||
Sur un balayage complet des huit paliers, **R500 commute sept fois, R1000 trois
|
||||
fois, R2000 une seule** : le bit de poids faible s'use sept fois plus vite que le
|
||||
bit de poids fort. Le comptage par relais devient **la seule façon de savoir
|
||||
quand le contacteur 500 W arrivera en fin de vie** — la puissance commutée, elle,
|
||||
ne le dit pas.
|
||||
|
||||
**ECS-307 — Grain des temporisations.** `minOn`/`minOff` sont aujourd'hui **par
|
||||
charge** (un unique `m_lastSwitch`, `relayrouter.h:107`), ce qui satisfait
|
||||
@ -376,7 +446,9 @@ mécanique reste **L0**. Perte de sonde → mode sans sonde, pas arrêt d'urgenc
|
||||
|---|---|---|
|
||||
| ECS-306 | simulation | `testEcsBudgetUnderLock` |
|
||||
| ECS-412 | simulation | `testEcsRebuildPreservesLock` |
|
||||
| ECS-302, ECS-305 | unitaire | `testEcsSwitchCost` |
|
||||
| ECS-303 | banc, puis simulation | relevé de commutations à la frontière 1500/2000 — **mesurer avant de coder** |
|
||||
| ECS-305 | unitaire | `testEcsSwitchCount` — compteur par relais |
|
||||
| ECS-302 | unitaire | `testEcsSwitchCost` — **non exerçable sur le câblage 500/1000/2000** (encodage unique) |
|
||||
| ECS-304 | unitaire | `testEcsLevelsPure` |
|
||||
| ECS-410 | simulation | `testEcsPartialFailure` |
|
||||
| ECS-411 | simulation | `testEcsRestartRecovery` |
|
||||
@ -445,36 +517,40 @@ protocole dans le même lot.**
|
||||
| 2026-08-08 | Recouvrement ECS-411 ↔ ECS-412 relevé → §13-2, bloque l'ouverture de l'étape 1 |
|
||||
| 2026-08-08 | Étape 2 : « type domaine » → « noyau de calcul » (collision avec `spec_loadmodel.md` LM-100) |
|
||||
| 2026-08-08 | ECS-110 : validation interdite de reposer sur `Q_ASSERT` (absent en release) |
|
||||
| 2026-08-08 | **§13-1 CLOS** — schéma « ARCHITECTURE CIBLE » d'`AGENTS.md` redessiné (`f6a4be5`) |
|
||||
| 2026-08-08 | **§13-2 CLOS, lecture (b)** — ECS-412 réduit à `m_lastSwitch` + cause racine (rebuild incrémental) ; ECS-411 remonté en étape 1 |
|
||||
| 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-08 | Câblage banc confirmé **500/1000/2000** — ECS-302 non exercé, ECS-305 relevé, **ECS-303 créé** (point dur 1500→2000) |
|
||||
|
||||
---
|
||||
|
||||
## §13 — Points ouverts
|
||||
|
||||
1. **Gouvernance** — redessiner le schéma « ARCHITECTURE CIBLE » d'`AGENTS.md`
|
||||
(`EcsRelayAdapter` et le kind `Stage` y figurent encore ; cf. §0). Le document
|
||||
normatif décrit des classes supprimées tant que ce n'est pas fait.
|
||||
2. **Ordonnancement ECS-411 ↔ ECS-412** — bloque l'ouverture de l'étape 1. Deux
|
||||
lectures :
|
||||
- **(a) garder l'ordre du §2** : ECS-412 traite `m_currentStage` et
|
||||
`m_lastSwitch` dès l'étape 1 ; ECS-411 en réécrira la moitié en étape 4.
|
||||
Fidèle au principe « les défauts de comportement passent devant », au prix
|
||||
d'un aller-retour.
|
||||
- **(b) réduire ECS-412 à `m_lastSwitch`** et remonter ECS-411 en étape 1 avec
|
||||
lui : ils partagent le chemin de reconstruction, et `m_currentStage` devient
|
||||
gratuit. Évite l'aller-retour, mais fait entrer en étape 1 une exigence que
|
||||
le §2 justifiait autrement.
|
||||
|
||||
Dans les deux cas, `testEcsRebuildPreservesLock` DOIT couvrir « `SetLoadConfig`
|
||||
pendant une fenêtre de verrou active » : c'est l'irréductible.
|
||||
3. **ECS-307** — grain des temporisations par relais : nécessaire, ou le compteur
|
||||
de commutations suffit-il ?
|
||||
4. **ECS-605** — asymétrie de l'hystérésis, à régler à l'usage.
|
||||
5. Câblage réel du banc : 500/1000/1500 ou 500/1000/2000. Ne bloque pas le code,
|
||||
mais le premier est le seul qui exerce ECS-302.
|
||||
6. **Conflit d'écriture** — `connectIO()` est le mécanisme natif si l'on veut un
|
||||
1. **ECS-307** — grain des temporisations par relais : nécessaire, ou le compteur
|
||||
de commutations suffit-il ? Le câblage binaire rend la question plus concrète
|
||||
(R500 commute 7× plus que R2000), sans la trancher.
|
||||
2. **ECS-303 / ECS-605** — largeur de l'hystérésis à la frontière 1500/2000 W,
|
||||
et asymétrie montée/descente. **À mesurer au banc avant de coder** : c'est le
|
||||
relevé de commutations qui donne le chiffre.
|
||||
3. **Conflit d'écriture** — `connectIO()` est le mécanisme natif si l'on veut un
|
||||
jour exposer un relais ECS au pilotage manuel de l'utilisateur. Ce serait
|
||||
alors un conflit d'écriture à arbitrer, pas une aide.
|
||||
|
||||
**CLOS le 2026-08-08 :**
|
||||
|
||||
- **§13-1 gouvernance** — le schéma « ARCHITECTURE CIBLE » d'`AGENTS.md` a été
|
||||
redessiné (lot de consolidation douze zones, commit `f6a4be5`) : `RelayRouter`
|
||||
et `EtmVariableLoadAdapter` y figurent, le kind `Stage` en est retiré,
|
||||
`SocketScheduler` et `BatteryAdapter` sont marqués non écrits.
|
||||
- **§13-2 ordonnancement** — **lecture (b) retenue**. ECS-412 est réduit à
|
||||
`m_lastSwitch` et redirigé vers sa cause racine (rebuild incrémental) ;
|
||||
ECS-411 remonte en étape 1. L'étape 1 porte donc trois exigences.
|
||||
`testEcsRebuildPreservesLock` couvre obligatoirement « `SetLoadConfig` pendant
|
||||
une fenêtre de verrou active ».
|
||||
- **Câblage du banc** — confirmé **500/1000/2000 W** (cf. §4). ECS-302 n'est pas
|
||||
exercé par ce matériel ; ECS-305 monte en importance ; ECS-303 créé.
|
||||
|
||||
**CLOS par l'audit** : le waterfall ne choisit pas entre encodages équivalents
|
||||
(il n'en voit qu'un) ; `ioConnections()`/`connectIO()` est un lien 1↔1
|
||||
état-à-état avec pour seule transformation un booléen `inverted` — il ne peut
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user