Ce qui ne dépend pas du canal, écrit maintenant. La lecture se branchera quand
la forme du canal openmeteo sera connue ; le magasin est vide d'ici là, et une
courbe vide EST la réponse honnête à « que prévoit-on ? » quand personne n'a
rien publié.
LA CLÉ EST LA PAIRE (targetAt, receivedAt). Avec targetAt seul, chaque
rafraîchissement écraserait le précédent : on perdrait les révisions, donc la
comparaison prévu/réalisé — ce que ce lot existe pour montrer. Le GPL n'apprend
pas, il rend l'apprentissage possible : cette table est la matière, et l'écraser
la détruirait pour toujours sans que rien ne le signale.
receivedAt, JAMAIS issuedAt, et l'interdiction est écrite et non seulement
nommée : aucun horizon ne se calcule depuis ce champ. Entre l'initialisation
d'un run et sa mise à disposition s'écoule la latence de publication du modèle —
des heures. targetAt - receivedAt est donc trop court, systématiquement et dans
le même sens. « Quelle prévision » est juste, « quel horizon » est faux.
LA DÉDUPLICATION PORTE SUR L'INTERSECTION, pas sur la charge utile. Si la
fenêtre glisse, la courbe de 11 h a un point de moins que celle de 10 h : une
égalité de charge utile verrait une révision toutes les heures, exactement ce
que la déduplication existe pour éviter. Et `communs > 0` est requis — sans
heure commune il n'y a rien à comparer, et conclure « identique » ferait
disparaître une révision dont on ne sait rien (cas du passage de minuit).
Ce choix ne dépend pas de la forme que le producteur publie aujourd'hui : il est
correct que la fenêtre glisse ou non, donc il ne peut pas être invalidé par un
changement chez le voisin dont personne ne nous préviendrait.
RÉTENTION : sept jours pleins, puis les DEUX BOUTS par heure visée — la première
révision (horizon le plus long) et la dernière (meilleure estimation). Les deux,
et pas une : elles répondent aux deux seules questions qui se posent, et n'en
garder qu'une détruirait la révision.
model reste NULL tant que le producteur publie une INTENTION et non une
provenance. Une chaîne vide serait déjà une valeur ; NULL dit « on ne sait pas »,
ce qui est vrai. Aucun remplissage rétroactif à prévoir.
GetPvForecast : un Get* SANS *Changed — amendement assumé à l'invariant 11, qui
vise l'ÉTAT DE L'ARBITRE, celui qui bouge à chaque cycle et laisse un client
déconnecté en retard. Une courbe historisée se lit sur demande, comme
GetPowerBalanceLogs ; notifier chaque révision produirait un message par heure
sur une donnée que personne n'attend en direct. `knownAt` rend la lecture
reproductible : « ce qu'on prévoyait ce matin » se demande en le fixant.
Base SÉPARÉE, jamais energylogs.sqlite : celle-là appartient à l'amont qui en
migre le schéma, et surtout la clé y est un instant de mesure quand la nôtre est
une paire. Une prévision révisée n'a rien à faire dans la base du mesuré.
Trois tests unitaires, et le premier vise le cas qu'un == naïf raterait : une
fenêtre glissée d'une heure, valeurs communes inchangées, charge utile
différente — ce n'est PAS une révision. Avec ses contre-épreuves : une valeur
commune modifiée EN EST une, et une fenêtre sans heure commune aussi.
loadmodel 29/29, charging 48/48, spotmarket 32/32, simulation 69/69,
ci-quality 4/4, build 0 erreur 0 warning.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015F7G5VeaPVSMeVNjiGj36p