docs(optimiseur): INTERFACE.md (état, réglage, plan publié), protocole §4/§7/§8 (brief 4 T4)

- INTERFACE.md : GetOptimizerStatus, SetOptimizerEndpoint, SetOptimizerToken, GetPlan,
  OptimizerStatusChanged, permissions, UpdateLearned ignoré ; ce que nymea journalise des
  paramètres d'un appel ; comment distinguer « pas d'optimiseur » de « pas de plan ce cycle ».
- OPTIMIZER_PROTOCOL.md : tier retiré (E23), « deux requêtes consécutives » (E17), §8 état 1.1.
- AGENTS.md invariant 6 : tier retiré.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Patrick Schurig 2026-10-17 09:34:39 +02:00
parent c1ce67942c
commit 7a32961b23

View File

@ -148,9 +148,10 @@ Réponse attendue :
- Le plugin appelle `GetPlan` à chaque cycle d'optimisation (~1/min) **et** sur - Le plugin appelle `GetPlan` à chaque cycle d'optimisation (~1/min) **et** sur
événement significatif — voir `trigger`, §5.1. événement significatif — voir `trigger`, §5.1.
- **Le heartbeat est implicite** : une réponse valide à `GetPlan` = optimiseur vivant. - **Le heartbeat est implicite** : une réponse valide à `GetPlan` = optimiseur vivant.
Deux cycles consécutifs sans réponse valide (timeout par requête : 10 s) → Deux **requêtes** consécutives en échec (timeout par requête : 10 s, erreur, réponse
`optimizerAlive = false`, repli rule-based, notification de changement de capacités illisible, plan invalide) → `optimizerAlive = false`, repli rule-based, notification
(namespace publié actuel : `NymeaEnergy`). `NymeaEnergy.OptimizerStatusChanged`. Le compte est **par requête, pas par cycle** : une
requête remplacée par une plus récente avant sa réponse n'est pas un échec.
- L'optimiseur peut être lent à *calculer* (EOS : minutes) — il doit alors répondre - L'optimiseur peut être lent à *calculer* (EOS : minutes) — il doit alors répondre
vite avec son **dernier plan en cache** ou `abstain`, et recalculer en tâche de fond. vite avec son **dernier plan en cache** ou `abstain`, et recalculer en tâche de fond.
Une requête n'attend jamais un calcul long. Le rythme de recalcul est libre Une requête n'attend jamais un calcul long. Le rythme de recalcul est libre
@ -478,21 +479,30 @@ stratégie rule-based **pour ce cycle**, sans considérer l'optimiseur comme mor
| Situation | optimizerAlive | Stratégie active | | Situation | optimizerAlive | Stratégie active |
|---|---|---| |---|---|---|
| Réponse plan valide | true | optimiseur | | Réponse plan valide | true | optimiseur |
| `abstain` | true | rules (ce cycle) | | `abstain` | inchangé | rules (ce cycle) |
| Plan expiré (`validUntil` dépassé) | true | rules jusqu'au prochain plan valide | | Plan expiré (`validUntil` dépassé) | inchangé | rules jusqu'au prochain plan valide |
| Timeout / erreur / plan invalide ×2 | false | rules | | Timeout / erreur / plan invalide sur **deux requêtes consécutives** | false | rules |
| Socket absent / fermé | false | rules | | Socket absent / fermé | false | rules |
| `optimizerExpected=false` (tier Community) | false | rules | | `optimizerExpected=false` (aucun endpoint configuré) | false | rules |
| Mode dégradé L2 (compteur muet > 90 s) | inchangé | **aucune** — planification suspendue, repli de sécurité | | Mode dégradé L2 (compteur muet > 90 s) | inchangé | **aucune** — planification suspendue, repli de sécurité |
L'objet capabilities exposé à l'app (`tier`, `optimizerExpected`, `optimizerAlive`, Retour à `optimizerAlive = true` au premier plan valide. Les échecs se comptent **par
`activeStrategy`) reflète cet état en continu ; tout changement émet une notification. requête** : une abstention ou un plan déjà expiré à la réception interrompent la série ; une
requête remplacée par une plus récente n'est pas un échec. Avant le premier plan valide,
`optimizerAlive` vaut `false`. Un plan invalide remplace le cache (l'ancien plan valide n'est
plus appliqué) et un socket fermé l'abandonne (E-T3-9).
L'état exposé à l'app (`optimizerExpected`, `optimizerAlive`, `activeStrategy` ∈
`optimizer` | `rules` | `none`) reflète cet état en continu ; chaque transition émet
`NymeaEnergy.OptimizerStatusChanged` (`INTERFACE.md`). Il n'y a pas de `tier` : l'app déduit ce
qu'elle affiche de ces trois champs.
## 8. Apprentissage et transparence ## 8. Apprentissage et transparence
- L'optimiseur peut renvoyer ses estimations (`learned` + `confidence`) dans une - L'optimiseur peut renvoyer ses estimations (`learned` + `confidence`) dans une
notification `UpdateLearned` (sans id, fire-and-forget) ; le plugin les persiste notification `UpdateLearned` (sans id, fire-and-forget) ; le plugin les persiste
et les retransmet dans les contextes suivants. et les retransmet dans les contextes suivants. **État 1.1 du moteur** : reçue, journalisée
une fois, ignorée — la persistance est un lot ultérieur (`INTERFACE.md`).
- Tant que `confidence < 0.7` (seuil indicatif), l'app peut afficher un badge - Tant que `confidence < 0.7` (seuil indicatif), l'app peut afficher un badge
« profil en apprentissage ». « profil en apprentissage ».
- Les données d'apprentissage restent **locales**. Rien ne quitte le site. - Les données d'apprentissage restent **locales**. Rien ne quitte le site.