Patrick Schurig da58d3906a feat(prévision PV): version B — une révision n'existe que si la prévision a changé
La déduplication porte sur les VALEURS ; un run qui reconduit ne crée pas de
ligne, il met à jour la fraîcheur.

Le raisonnement qui tranche est celui de meta.json, appliqué une seconde fois :
on ne stocke pas pour un consommateur qui n'existe pas. L'apprentissage n'existe
pas, le consommateur d'aujourd'hui est un affichage, et un affichage ne lit pas
une confirmation. Même déclencheur de réouverture, pour la même raison.

UNE CONTRADICTION DANS LA CONSIGNE, ET COMMENT ELLE EST LEVÉE. Deux instructions
incompatibles : « issuedAt est mis à jour sur la ligne existante » (= le dernier
run) et « il devient l'horodatage du premier run, pas du dernier » (= le
premier). Et l'avertissement joint — « quelqu'un lira prévu à 06Z sur une ligne
que le run 12Z a confirmée » — ne décrit un danger QUE si l'instant n'est pas
mis à jour.

Aucune des deux n'a été choisie, parce qu'aucune ne suffit : un seul champ ne
peut pas porter les deux faits sans mentir sur l'un. Ce serait exactement le
piège receivedAt/issuedAt de la veille — deux instants voisins, un nom qui
promet le mauvais — et le remède est le même : nommer ce que c'est.

  firstIssuedAt    run qui a produit ces valeurs EN PREMIER  (CLÉ, immuable)
  lastConfirmedAt  dernier run qui les a RECONDUITES         (mis à jour)

La FRAÎCHEUR est lastConfirmedAt ; l'ANCIENNETÉ DE L'AVIS est firstIssuedAt.
Une valeur émise à 06Z et reconduite à 12Z est fraîche ; une valeur émise à 06Z
que personne n'a reconduite EST vieille de six heures. Les deux phrases sont
vraies et ne parlent pas de la même chose. Les deux sont publiées au contrat :
n'en publier qu'une obligerait le client à choisir laquelle il croit.

firstIssuedAt ne bouge jamais — une clé qui se déplace change l'identité de la
ligne sous elle. Les deux instants coïncident à l'ouverture et divergent à la
première reconduction, ce que le test vérifie explicitement.

DERNIER REMPART, PAS FILET : celle du producteur compare la charge utile
entière, où issuedAt figure, donc elle ne mord jamais sur ce cas. Si celle-ci se
trompe, personne ne rattrape — raison de plus pour qu'elle ne dépende d'aucun
comportement du voisin : intersection des créneaux, et communs > 0 requis.

loadmodel 30/30, 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
2026-09-08 19:55:38 +02:00
..