Jalon : le tableau de bord sur données réelles, et la prévision qui écrit enfin #193

Merged
lenaic merged 20 commits from develop into main 2026-09-08 10:56:40 +00:00
Owner

20 commits. Deux d'entre eux lèvent les deux points bloquants du pack de liens demandé par le formateur pour EC05 et EC06.

Ce que ça change sur le serveur

Le tableau de bord quitte les données inventées (#182). Cinq lectures sur huit lisent PostgreSQL : les alertes, la traçabilité d'une valeur, le journal de collecte et les deux courbes. L'écran Qualité servira les 14 431 alertes réelles de la base au lieu des quatre écrites en dur.

Le job de prévision se met à écrire (#192). Il exigeait ses vingt-quatre retards jusqu'à l'heure précédente, que mesure_horaire ne matérialise jamais à temps : sept sites écartés, zéro ligne, à chaque passe. Éprouvé contre enervision_prod avant d'approuver :

instant visé : 2026-09-08 11:00:00+00:00
les 7 sites   dernier seau 07:00   décalage utilisable : 3   (tolérance 6)

Les sept sites trouvent leurs vingt-quatre heures consécutives, avec trois heures de marge. public.prevision cessera d'être vide, et R1 cessera de rendre sept indécisions par passe faute de prévision.

La socket Docker ne fuit plus dans les conteneurs de tâche (#189). Le serveur tourne encore avec docker_host: "unix:///var/run/docker.sock", qui donne un accès root à n'importe quelle poussée. Attention : ce correctif demande --tags ci, que le déploiement continu ne joue jamais, et c'est délibéré — la chaîne tourne sur cet exécuteur. C'est donc un geste manuel à faire après.

Deux manuels d'exploitation (#187, #191) : le réentraînement, quatrième geste sensible d'ENF-16 qui n'existait pas, et la correction du rejeu du collecteur trouvée par Justine en le rejouant sans aide.

Ce que ça ne pose pas

migrations            aucune
clés de coffre        aucune nouvelle
tâches de cron        aucune ajoutée
piles recréées        api et supervision seulement

Déploiement court, cinq minutes environ. PostgreSQL n'est pas recréé pour cause de montage de fichier ; il l'est de toute façon par build: true, comportement inchangé.

Critères de sortie

  • Le déploiement sort en failed=0
  • https://app.g2.enervision se charge et l'écran Qualité montre des alertes issues de la base
  • À la passe de :35, public.prevision n'est plus vide
  • À la passe de :45, R1 ne rend plus « grandeurs absentes : prevision_kw »
  • La relève de la minute n'a pas de trou pendant la bascule

Ce qui reste après

--tags ci à jouer à la main pour le #103. Et la #190, les sections « quand ça échoue » de reprise.md et deploiement.md, qui attend un relecteur.

20 commits. Deux d'entre eux lèvent les deux points bloquants du pack de liens demandé par le formateur pour EC05 et EC06. ## Ce que ça change sur le serveur **Le tableau de bord quitte les données inventées** (#182). Cinq lectures sur huit lisent PostgreSQL : les alertes, la traçabilité d'une valeur, le journal de collecte et les deux courbes. L'écran Qualité servira les **14 431 alertes réelles** de la base au lieu des quatre écrites en dur. **Le job de prévision se met à écrire** (#192). Il exigeait ses vingt-quatre retards jusqu'à l'heure précédente, que `mesure_horaire` ne matérialise jamais à temps : sept sites écartés, zéro ligne, à chaque passe. Éprouvé contre `enervision_prod` avant d'approuver : ``` instant visé : 2026-09-08 11:00:00+00:00 les 7 sites dernier seau 07:00 décalage utilisable : 3 (tolérance 6) ``` Les sept sites trouvent leurs vingt-quatre heures consécutives, avec trois heures de marge. `public.prevision` cessera d'être vide, et R1 cessera de rendre sept indécisions par passe faute de prévision. **La socket Docker ne fuit plus dans les conteneurs de tâche** (#189). Le serveur tourne encore avec `docker_host: "unix:///var/run/docker.sock"`, qui donne un accès root à n'importe quelle poussée. Attention : ce correctif demande `--tags ci`, que le déploiement continu ne joue **jamais**, et c'est délibéré — la chaîne tourne sur cet exécuteur. C'est donc un geste manuel à faire après. **Deux manuels d'exploitation** (#187, #191) : le réentraînement, quatrième geste sensible d'ENF-16 qui n'existait pas, et la correction du rejeu du collecteur trouvée par Justine en le rejouant sans aide. ## Ce que ça ne pose pas ``` migrations aucune clés de coffre aucune nouvelle tâches de cron aucune ajoutée piles recréées api et supervision seulement ``` Déploiement court, cinq minutes environ. PostgreSQL n'est pas recréé pour cause de montage de fichier ; il l'est de toute façon par `build: true`, comportement inchangé. ## Critères de sortie - [ ] Le déploiement sort en `failed=0` - [ ] `https://app.g2.enervision` se charge et l'écran Qualité montre des alertes issues de la base - [ ] À la passe de `:35`, `public.prevision` n'est plus vide - [ ] À la passe de `:45`, R1 ne rend plus « grandeurs absentes : prevision_kw » - [ ] La relève de la minute n'a pas de trou pendant la bascule ## Ce qui reste après `--tags ci` à jouer à la main pour le #103. Et la #190, les sections « quand ça échoue » de `reprise.md` et `deploiement.md`, qui attend un relecteur.
lenaic self-assigned this 2026-09-08 10:37:45 +00:00
Deux fondations, sans lesquelles le branchement du tableau de bord sur la zone
or ne tient pas.

`Methode` passe de trois à QUATRE valeurs. La forme réduite faisait porter à
`none` deux sens distincts — « valeur mesurée » et « trou assumé » — alors que
l'ADR 0006 et le §9 du document data nomment `measured` le régime de la valeur
présente et valide. La contrainte de `public.mesure` accepte les quatre depuis
la migration 0010, et la zone argent les écrit : sur une journée réelle,
**89,5 % des lignes portent `measured`**. Tant que le dépôt servait des
fixtures, l'énumération n'a jamais vu de ligne réelle ; le jour où `repository`
lit la base, une valeur hors énumération fait échouer la validation Pydantic et
l'endpoint rend 500 — sur la grande majorité des lignes, pas sur un cas limite.

`FakeConn` sait désormais répondre à des requêtes. Elle n'avait que `commit` et
`rollback`, ce qui suffisait tant que `repository` ignorait son paramètre `conn`.
Les réponses sont appariées par FRAGMENT de requête et non par égalité : écrire
le SQL complet dans chaque cas en ferait la copie d'un fichier de production que
personne ne relirait, et au premier `order by` déplacé tous les cas rougiraient
sans qu'aucun comportement n'ait changé.

Ce n'est pas un simulateur de PostgreSQL et le docstring le dit : l'exactitude
du SQL se prouve en intégration, la projection des lignes en modèles se prouve
en unitaire. Confondre les deux donnerait un faux qui ment sur ce qu'il garantit.

SON CONSTRUCTEUR RESTE SANS PARAMÈTRE, et c'est écrit en commentaire. `conftest`
passe la classe à `app.dependency_overrides[get_db]`, et FastAPI lit la
signature de `__init__` comme celle d'une dépendance : un argument de
configuration y devient un champ de corps de requête, et toute route POST rend
422 sur un corps parfaitement valide. Constaté, 84 tests en erreur. L'instance
est donc partagée par une fixture et scriptée après coup.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014e7YA7QikrPg6wU3U1ur9M
`alertes`, `tracabilite_site`, `journal_collecte_site`, `serie_site` et
`serie_parc` lisent PostgreSQL. Les trois qui restent — `sites`,
`synthese_parc`, `recommandations_site` — portent la prévision et les
recommandations, dont les tables sont vides : elles suivront le #37 et le #39.

LE FILTRE PAR SITE EST DANS LA REQUÊTE, pas appliqué après coup. C'est le
contrôle d'accès à la ressource de l'ENF-02 : le faire en Python ramènerait
d'abord en mémoire des lignes que l'appelant n'a pas le droit de voir.

Les valeurs des courbes viennent de `mesure_horaire`, l'agrégat continu de la
migration 0016, et JAMAIS d'une moyenne recalculée ici — le §12 du document data
pose la règle, « les recalculer donnerait deux vérités pour une question ».
Mais l'agrégat ne porte pas l'imputation, or le front trace ces segments
autrement (EF-04, « la méthode reste lisible à côté de la valeur ») : d'où une
jointure sur `public.mesure` qui ne sert QU'À CE DRAPEAU.

`reference_kw` est la PERSISTANCE — la valeur observée à la même heure la
veille. Ce n'est pas le modèle du #36, c'est le repère naïf contre lequel il se
mesure, et c'est ce que le schéma de sortie nomme depuis le début. Elle se
calcule depuis `mesure_horaire` seule, sans attendre le service d'inférence. Un
historique trop court rend zéro plutôt qu'une erreur : la courbe reste
affichable, seul le repère manque.

`prevision` devient FACULTATIVE dans le contrat. `public.prevision` est vide :
le modèle H+1 est entraîné et promu (#36), rien ne sert encore ses valeurs
(#37). Un champ absent dit « pas encore de modèle » ; un chiffre dérivé d'autre
chose serait invérifiable, et la première question du jury serait d'où il sort.
`ecart_reference_pct` suit : sans les deux termes il n'y a pas d'écart, et
rendre zéro laisserait croire que la prévision colle à la référence.

Deux absences ont deux sens, et les confondre coûterait cher en exploitation.
En traçabilité : aucune ligne rend 404 (« cet instant n'est pas dans la
fenêtre »), une ligne sans valeur retenue rend 503 (« la minute existe, rien n'y
a été mesuré » — le trou assumé du §9). En série : un site sans point rend 503
et non une courbe vide, qui se lirait « ce site ne consomme rien » alors qu'on
ne sait pas.

UN FAUX POSITIF DÉCOUVERT EN CHEMIN. Le cas
`test_repartition_methode_couvre_les_releves_attendus` affirmait
« somme(répartition) + manquants == attendus ». C'était vrai du jeu figé, FAUX
de la donnée réelle : la zone or calcule les manquants comme
« attendus − mesurées − imputées », et la répartition compte les quatre régimes
sur TOUTE la série — les additionner comptait `none` deux fois. Le vrai
invariant est « somme == attendus » ET « répartition['none'] == manquants »,
vérifié sur SITE006 le 2026-09-06 : 1317 + 122 + 1 = 1440, manquants 1.

Et un piège de projection qu'un cas garde nommément : `qualite_jour.
taux_disponibilite` est stocké en FRACTION — 0,9993 — pour la même raison que le
taux de charge dans la 0012. Oublier le facteur cent aurait affiché « 1,0 % de
disponibilité » sur une journée parfaite.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014e7YA7QikrPg6wU3U1ur9M
dashboard : l'écran Site dit qu'il n'a pas de prévision (#179)
Some checks failed
Intégration / Contrôles statiques du dépôt (pull_request) Successful in 8s
Intégration / Workflows — lint et audit de sécurité (pull_request) Successful in 22s
Intégration / Tableau de bord — dépendances, tests et construction (pull_request) Successful in 37s
Intégration / Python — qualité, tests et dépendances (pull_request) Failing after 1m16s
b6ce34a462
`store.serie.prevision.valeur_kw` déréférençait sans garde un champ que l'API
rend désormais `null` tant que le service d'inférence ne sert pas les valeurs du
modèle promu (#36, #37).

L'écran affiche « — » et la raison, plutôt que de planter ou d'afficher un
chiffre dont personne ne pourrait dire d'où il sort. `ecart_reference_pct` est
gardé de la même façon : sans prévision, il n'y a pas d'écart.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014e7YA7QikrPg6wU3U1ur9M
api: ruff format sur le dictionnaire des fenêtres (#179)
All checks were successful
Intégration / Tableau de bord — dépendances, tests et construction (pull_request) Successful in 38s
Intégration / Contrôles statiques du dépôt (pull_request) Successful in 6s
Intégration / Workflows — lint et audit de sécurité (pull_request) Successful in 17s
Intégration / Python — qualité, tests et dépendances (pull_request) Successful in 5m14s
9cb280d33d
`_FENETRES` tenait sur deux lignes là où le formateur en veut cinq. Sans
conséquence sur le comportement, mais `ruff format --check services packages`
est une tâche BLOQUANTE de la chaîne : elle aurait arrêté la demande de fusion.

La faute est d'avoir relancé `ruff check` après les dernières modifications sans
relancer `ruff format`. Les deux sont bloquants, ils se vérifient ensemble.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014e7YA7QikrPg6wU3U1ur9M
api: la courbe du parc somme les sites — relecture de la #182
Some checks failed
Intégration / Tableau de bord — dépendances, tests et construction (pull_request) Failing after 36s
Intégration / Workflows — lint et audit de sécurité (pull_request) Successful in 19s
Infra Ansible / Playbooks Ansible valides (pull_request) Successful in 1m34s
Intégration / Python — qualité, tests et dépendances (pull_request) Successful in 5m26s
Intégration / Contrôles statiques du dépôt (pull_request) Successful in 6s
40a68f23d5
Trois défauts relevés en relecture, dont un bloquant, plus le cas d'intégration
qui manquait pour que la classe entière ne puisse plus repasser.

1. BLOQUANT — /v1/parc/mesures n'agrégeait pas les sites.

`mesure_horaire` porte une ligne par (site_id, heure). Un
`group by heure, moyenne_kw` ne regroupe donc RIEN : le couple est distinct par
site, et la courbe du parc sortait avec autant de points par heure qu'il y a de
sites, chacun à l'échelle d'un seul. Vérifié sur la préproduction, fenêtre 24 h,
sept sites : 28 points au lieu de 4, chaque heure répétée sept fois.

La fenêtre journalière était fausse autrement : `avg` sur toutes les lignes de
tous les sites rendait la consommation d'un site, quand `capacite_totale_kw` et
la référence sont des sommes. La courbe et ses deux repères n'étaient pas à la
même échelle, d'un facteur voisin du nombre de sites.

Les deux requêtes agrègent maintenant en deux temps : somme sur les sites, puis
regroupement. Et la jointure d'imputation sort de l'agrégat — jointe, elle
multipliait chaque ligne horaire par ses soixante minutes AVANT le regroupement,
ce qui pondérait chaque heure par son nombre de minutes présentes et faisait
300 000 lignes jointes par appel sur 30 jours.

2. Le front ignorait `measured` et gardait l'ancien sens de `none`.

`glossaire.js` traduisait `none` par « mesure directe ». La base dit l'inverse
depuis la 0010. L'écran Qualité — celui qui parle de la qualité de la donnée —
affichait donc « 1317 en measured », en anglais, à côté de « 1 en mesure
directe » pour le trou. Les deux entrées sont posées, et le docstring de
`QualiteJour` qui portait encore l'ancien sens est corrigé.

3. /v1/alertes ramenait 1 742 lignes pour en afficher un compte.

La liste est bornée à 200, et la ventilation du pavé du parc passe par un
`count(*) ... group by severite` au lieu de filtrer la liste en Python. C'est
l'index partiel `alerte_ouvertes` de la 0018 qui sert, posé exactement pour ça.

LE CAS D'INTÉGRATION QUI MANQUAIT. Le docstring de `FakeConn` renvoyait à
`tests/integration/` pour l'exactitude du SQL, et aucun test d'intégration
n'exécutait ces requêtes : la promesse n'était tenue par personne, et c'est
pourquoi rien n'a vu le défaut — un double rend ce qu'on lui donne.
`tests/integration/test_lectures_tableau_de_bord.py` pose deux sites sur deux
heures avec des valeurs qui distinguent d'un coup d'oeil une somme (300, 330),
une moyenne (150, 165) et une absence de regroupement (quatre points). Cinq cas,
joués contre la préproduction.

CE QUE LA VÉRIFICATION A RÉVÉLÉ EN PLUS. Le drapeau d'imputation disait
`<> 'measured'` ; la préproduction porte **347 574 lignes `none` avec une
valeur**, antérieures à la normalisation de la 0010 et jamais reprises. Sur 24 h
le drapeau restait juste, sur 30 jours chaque journée serait ressortie imputée.
Le prédicat nomme désormais les deux régimes d'imputation — ce qui est aussi
plus juste : un trou n'est pas une reconstitution.

Deux descriptions OpenAPI avaient vieilli dans le même commit, le commentaire de
`_POINT_ABSENT` contredisait son code — c'était le commentaire qui avait tort —
et trois réenroulements à 88 colonnes, hors du périmètre annoncé, sont annulés.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014e7YA7QikrPg6wU3U1ur9M
docs: manuel de réentraînement, le quatrième geste d'ENF-16 (#44)
All checks were successful
Intégration / Tableau de bord — dépendances, tests et construction (pull_request) Successful in 35s
Intégration / Contrôles statiques du dépôt (pull_request) Successful in 6s
Intégration / Workflows — lint et audit de sécurité (pull_request) Successful in 17s
Intégration / Python — qualité, tests et dépendances (pull_request) Successful in 5m36s
ec99fd1ec5
Le README de services/inference/ décrit ce que le paquet fait ; un manuel
décrit ce qu'on tape quand ça part mal. docs/runbooks/reentrainement.md
reprend la séquence d'entraînement et la promotion — le geste manuel reste
le §5 de mlflow.md — et ajoute la section « Quand ça échoue » : les codes
2, 3 et 4 du lanceur, la promotion refusée hors enervision_prod, MLflow
injoignable, et le modèle qui ne bat pas la persistance.

Ce dernier cas sort en code 0 : ce n'est pas une panne, c'est un résultat.
Le manuel dit quoi faire — l'alias n'a pas bougé, donc il n'y a rien à
replier ; on consigne le chiffre, la fenêtre et la base, et on cherche
pourquoi ensuite.

Trois faits qui n'étaient écrits nulle part :

- le venv du serveur est /opt/enervision/venv et non venv-model. Suivre la
  lettre de l'ancien README sortait en code 3 sur un chemin inexistant. Le
  README et l'en-tête du lanceur sont corrigés ici, sinon le manuel et le
  paquet se contrediraient.
- « mêmes bornes » n'est pas « mêmes données » : mesure_horaire est un
  agrégat continu qui se complète en arrière — 10 283 puis 10 290 heures à
  dix minutes d'écart sur des bornes identiques. Sans ce fait, on cherche un
  non-déterminisme dans le régresseur alors qu'il est dans la base.
- avec --deux-passes, --jusqu-a doit s'écrire avec le signe égal. Le lanceur
  ne reconnaît que cette forme pour figer les bornes ; en deux mots, il
  ajoute la sienne, qui l'emporte, et la fenêtre demandée est remplacée en
  silence.

Le tableau final dit ce qui n'a pas été rejoué sur le serveur, comme le §5
de mlflow.md le fait déjà : le code 4 n'a jamais été observé et ne se
provoque pas à la demande, et trois cas restent à jouer.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
dashboard : les quatre régimes, jusque dans les jeux d'essai (#179)
All checks were successful
Intégration / Tableau de bord — dépendances, tests et construction (pull_request) Successful in 47s
Intégration / Contrôles statiques du dépôt (pull_request) Successful in 6s
Intégration / Workflows — lint et audit de sécurité (pull_request) Successful in 18s
Infra Ansible / Playbooks Ansible valides (pull_request) Successful in 1m5s
Intégration / Python — qualité, tests et dépendances (pull_request) Successful in 5m40s
32097f3661
La chaîne a rougi sur quatre cas du tableau de bord : `glossaire.js` a gagné
`measured` et rendu à `none` son sens de trou assumé, mais les cas qui
l'exercent affirmaient encore l'ancien. Faute de ma part — j'ai modifié le
glossaire sans rejouer la suite du tableau de bord derrière.

DEUX JEUX D'ESSAI SE CONTREDISAIENT, et c'est plus intéressant qu'un simple
renommage.

`JournalCollecte` posait `repartition_methode: { none: 1050, interpolated: 147 }`
avec `releves_manquants: 243` et `releves_attendus: 1440`. Sous le sens de la
migration 0010, `none` est le trou assumé : la somme aurait dû faire les
attendus, et `none` valoir les manquants. La forme juste est donc
`{ measured: 1050, interpolated: 147, none: 243 }` — 1050 + 147 + 243 = 1440,
et `none` = 243 = les manquants. C'est exactement l'invariant relevé sur
SITE006 en production, qui est justement le site de ce jeu.

`TracabiliteDetail` posait `methode: 'none'` avec `valeur_brute_kw: 104`. Un
trou assumé n'a par définition aucune valeur : la fixture décrivait une ligne
qui ne peut pas exister. C'est `measured`.

Les deux ne « passaient » que parce que le front traduisait `none` par « mesure
directe ». Corriger le glossaire les a mis à nu, ce qui est le comportement
attendu d'un jeu d'essai qui dit la vérité de la donnée.

`descriptionMethode('measured')` garde la formulation que l'équipe avait
écrite — « mesure directe, aucune reconstitution » — déplacée sur la bonne clé
plutôt que réécrite : elle s'accorde avec le libellé court.

373 cas du tableau de bord, 915 unitaires Python, ruff check et format.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
infra: le socket Docker ne fuit plus dans les conteneurs de tâche (#103)
All checks were successful
Intégration / Tableau de bord — dépendances, tests et construction (pull_request) Successful in 43s
Intégration / Contrôles statiques du dépôt (pull_request) Successful in 8s
Intégration / Workflows — lint et audit de sécurité (pull_request) Successful in 19s
Infra Ansible / Playbooks Ansible valides (pull_request) Successful in 1m24s
Intégration / Python — qualité, tests et dépendances (pull_request) Successful in 5m33s
3bde1cdffc
container.docker_host portait le chemin explicite du socket, ce qui fait
partager ce même socket par forgejo-runner avec chaque conteneur de tâche,
sans qu'aucun workflow ne l'ait demandé — accès root sur le serveur depuis
n'importe quelle poussée. La valeur "-" garde l'accès du runner lui-même,
nécessaire pour créer ses conteneurs, sans le repartager.

- runner-config.yml : docker_host à "-"
- rôle ci_runner : garde-fou Ansible sur cette valeur
- docs/FORGE.md : le risque concernait les tâches, pas seulement le runner
- workflow d'essai manuel (verif-socket-docker.yml) pour le prouver

Reste à appliquer sur le serveur : ansible-playbook site.yml --tags ci.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NECwhiHyPhse3ZiCeYnr53
[44] Rejeu runbook collecteur et correction
All checks were successful
Intégration / Contrôles statiques du dépôt (pull_request) Successful in 10s
Intégration / Tableau de bord — dépendances, tests et construction (pull_request) Successful in 38s
Intégration / Workflows — lint et audit de sécurité (pull_request) Successful in 20s
Infra Ansible / Playbooks Ansible valides (pull_request) Successful in 1m42s
Intégration / Python — qualité, tests et dépendances (pull_request) Successful in 5m44s
41c397d844
Fusionne develop : la prévision du #37 rencontre les lectures de la zone or
All checks were successful
Intégration / Contrôles statiques du dépôt (pull_request) Successful in 9s
Intégration / Workflows — lint et audit de sécurité (pull_request) Successful in 22s
Intégration / Tableau de bord — dépendances, tests et construction (pull_request) Successful in 40s
Intégration / Python — qualité, tests et dépendances (pull_request) Successful in 5m42s
6f43828ecc
La #185 a livré le service de prévision et rempli `public.prevision`, la table
vide sur laquelle cette branche avait fondé sa décision de rendre le champ
facultatif. Huit fichiers en conflit, tous ceux que les deux lots touchaient.

CONVERGENCE, PAS DÉSACCORD. Justine a rendu `prevision`, `prevision_kw` et
`reference_kw` facultatifs de son côté, exactement comme ici — deux fois la même
décision, prise séparément. Le conflit était donc mécanique sur le contrat.

CE QUE SA VERSION APPORTE ET QUE JE PRENDS. `reference_kw` vient désormais de
`public.prevision` : le job la publie AVEC la prévision qu'elle sert à juger
(persistance naïve, EF-07). Mon `SQL_REFERENCE` la recalculait depuis
`mesure_horaire`, faute de table remplie — c'était honnête tant qu'aucune
n'existait, ça donnerait maintenant un second chiffre pour la même grandeur, et
les deux divergeraient à la première correction du job. La requête et sa
fonction sont supprimées, ainsi que les deux cas qui les gardaient.

Son `_ecart_pct` factorisé remplace mes deux propriétés dupliquées, et sa
formulation de l'écran — « la passe horaire n'a rien écrit pour ce site » — est
plus juste que la mienne, qui disait « pas encore servi » alors que le service
existe depuis sa demande. Son `!= null` attrape aussi `undefined`.

CE QUE CETTE BRANCHE GARDE. Les points des deux courbes viennent de la base et
non des fixtures, avec l'agrégation en deux temps et la sous-requête
d'imputation. `points_imputes` se compte sur les points rendus plutôt que sur
`fixtures.SITE_HEURES_IMPUTEES`. `pointe` et `creux` gardent leur garde de
séquence vide.

LES DEUX DOUBLES DE CONNEXION COEXISTENT, parce que le dépôt emploie deux
protocoles : `cursor()` pour les prévisions (#37), `execute()` pour les lectures
scriptées (#179). `FakeConn` porte les deux et garde la RÉFÉRENCE de la liste de
prévisions au lieu de la copier — sans quoi la fixture `passe_horaire`, qui
l'étend après le montage, n'aurait aucun effet sur une instance partagée.
`previsions if previsions is not None else []` et non `or []` : une liste vide
est fausse, et `or` en fabriquerait une neuve dans le cas le plus courant.

La connexion porte désormais une courbe de vingt-quatre heures PAR DÉFAUT : une
base qui a des mesures est l'état normal, et un cas qui parle de la prévision
n'a pas à scripter des points pour y arriver. Elle est ancrée sur
`fixtures.INSTANT_REFERENCE`, dont `INSTANT_PREVU` dérive aussi — deux dates
indépendantes faisaient échouer « le point H+1 tombe après le dernier mesuré »
sans que le cas ait tort.

Six cas retirés : les deux qui gardaient mon calcul de persistance, et quatre
qui lisaient un profil de fixtures que la base remplace. Deux doublons de nom,
artefacts de la fusion, masquaient silencieusement les miens — dont un portait
un `fiche` jamais défini.

1 060 unitaires Python, 380 du tableau de bord, 5 d'intégration contre la
préproduction, ruff check et format, mypy strict.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Merge pull request 'infra : le socket Docker ne fuit plus dans les conteneurs de tâche (#103)' (#189) from gabriel/103-retirer-socket-taches into develop
Some checks failed
Intégration / Tableau de bord — dépendances, tests et construction (push) Successful in 42s
Intégration / Contrôles statiques du dépôt (push) Successful in 6s
Intégration / Workflows — lint et audit de sécurité (push) Successful in 21s
Infra Ansible / Playbooks Ansible valides (push) Successful in 1m26s
Intégration / Python — qualité, tests et dépendances (push) Has been cancelled
73635befce
Reviewed-on: https://10.105.200.41/g2/enervision/pulls/189
Reviewed-by: lenaic <lenaic@noreply.10.105.200.41>
Merge branch 'develop' into marvin/179-tableau-de-bord-zone-or
Some checks failed
Intégration / Python — qualité, tests et dépendances (pull_request) Has been cancelled
Intégration / Tableau de bord — dépendances, tests et construction (pull_request) Has been cancelled
Intégration / Contrôles statiques du dépôt (pull_request) Has been cancelled
Intégration / Workflows — lint et audit de sécurité (pull_request) Has been cancelled
12eb16a488
Merge pull request '[EF-10] Le tableau de bord lit la zone or : cinq lectures quittent les fixtures (#179)' (#182) from marvin/179-tableau-de-bord-zone-or into develop
All checks were successful
Intégration / Tableau de bord — dépendances, tests et construction (push) Successful in 41s
Intégration / Contrôles statiques du dépôt (push) Successful in 8s
Intégration / Workflows — lint et audit de sécurité (push) Successful in 18s
Intégration / Python — qualité, tests et dépendances (push) Successful in 5m35s
d753bebcbd
Reviewed-on: https://10.105.200.41/g2/enervision/pulls/182
Reviewed-by: lenaic <lenaic@noreply.10.105.200.41>
[37] Correctif job prevision
All checks were successful
Intégration / Contrôles statiques du dépôt (pull_request) Successful in 8s
Intégration / Workflows — lint et audit de sécurité (pull_request) Successful in 24s
Intégration / Tableau de bord — dépendances, tests et construction (pull_request) Successful in 39s
Infra Ansible / Playbooks Ansible valides (pull_request) Successful in 1m40s
Intégration / Python — qualité, tests et dépendances (pull_request) Successful in 5m57s
6dbb996768
Merge branch 'develop' into justine/37-correctif-fenetre-entree
All checks were successful
Intégration / Tableau de bord — dépendances, tests et construction (pull_request) Successful in 38s
Intégration / Contrôles statiques du dépôt (pull_request) Successful in 7s
Intégration / Workflows — lint et audit de sécurité (pull_request) Successful in 20s
Intégration / Python — qualité, tests et dépendances (pull_request) Successful in 5m55s
5fae8072ad
Merge branch 'develop' into justine/44-rejeu-collecteur
All checks were successful
Intégration / Tableau de bord — dépendances, tests et construction (pull_request) Successful in 39s
Intégration / Contrôles statiques du dépôt (pull_request) Successful in 6s
Intégration / Workflows — lint et audit de sécurité (pull_request) Successful in 17s
Intégration / Python — qualité, tests et dépendances (pull_request) Successful in 5m36s
62d035ff4b
Merge pull request '[44] Rejeu runbook collecteur et correction' (#191) from justine/44-rejeu-collecteur into develop
All checks were successful
Intégration / Contrôles statiques du dépôt (push) Successful in 8s
Intégration / Tableau de bord — dépendances, tests et construction (push) Successful in 37s
Intégration / Workflows — lint et audit de sécurité (push) Successful in 28s
Intégration / Python — qualité, tests et dépendances (push) Successful in 5m52s
d6759c92a3
Reviewed-on: https://10.105.200.41/g2/enervision/pulls/191
Reviewed-by: lenaic <lenaic@noreply.10.105.200.41>
Merge branch 'develop' into justine/37-correctif-fenetre-entree
All checks were successful
Intégration / Tableau de bord — dépendances, tests et construction (pull_request) Successful in 38s
Intégration / Contrôles statiques du dépôt (pull_request) Successful in 8s
Intégration / Workflows — lint et audit de sécurité (pull_request) Successful in 18s
Intégration / Python — qualité, tests et dépendances (pull_request) Successful in 5m27s
c971c8a134
Merge pull request '[37] Correctif job prevision' (#192) from justine/37-correctif-fenetre-entree into develop
Some checks failed
Intégration / Contrôles statiques du dépôt (push) Successful in 8s
Intégration / Workflows — lint et audit de sécurité (push) Successful in 28s
Intégration / Tableau de bord — dépendances, tests et construction (push) Successful in 40s
Intégration / Python — qualité, tests et dépendances (push) Has been cancelled
d395799b31
Reviewed-on: https://10.105.200.41/g2/enervision/pulls/192
Reviewed-by: lenaic <lenaic@noreply.10.105.200.41>
Merge pull request '[44] Manuel de réentraînement : la séquence, la promotion, et « quand ça échoue »' (#187) from olivier/44-runbook-reentrainement into develop
All checks were successful
Intégration / Contrôles statiques du dépôt (push) Successful in 7s
Intégration / Tableau de bord — dépendances, tests et construction (push) Successful in 36s
Intégration / Workflows — lint et audit de sécurité (push) Successful in 28s
Intégration / Tableau de bord — dépendances, tests et construction (pull_request) Successful in 36s
Intégration / Contrôles statiques du dépôt (pull_request) Successful in 7s
Intégration / Workflows — lint et audit de sécurité (pull_request) Successful in 22s
Infra Ansible / Playbooks Ansible valides (pull_request) Successful in 1m4s
Intégration / Python — qualité, tests et dépendances (push) Successful in 5m45s
Intégration / Python — qualité, tests et dépendances (pull_request) Successful in 5m45s
ed85ce0ab7
lenaic requested review from gabriel 2026-09-08 10:37:46 +00:00
gabriel approved these changes 2026-09-08 10:48:37 +00:00
lenaic merged commit 45a3c2a007 into main 2026-09-08 10:56:40 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
g2/enervision!193
No description provided.