Mise en production : la refonte du tableau de bord, l'ETL au quart d'heure et les correctifs front #220

Merged
lenaic merged 49 commits from develop into main 2026-09-08 21:44:08 +00:00
Owner

Ce que ça change

Mise en production de tout ce qui a été fusionné dans develop depuis le
07/09 : la refonte du tableau de bord, l'ETL au quart d'heure, la supervision
des conteneurs réparée, les manuels d'exploitation, et les trois correctifs
front de la soirée. C'est le rang 1 du backlog et le critère de sortie
ajouté au jalon J2 : main = develop.

Refs #48

Preuve

24 fusions, 16 demandes de fusion
135 fichiers, +8 461 / −1 500

Les demandes livrées :

#21  #44  #184 #186 #196 #203 #204 #206
#207 #209 #210 #214 #216 #217 #218 #219

Chacune est entrée dans develop avec ses quatre tâches de chaîne au vert et
l'approbation d'un pair. Aucune n'a été forcée.

Une migration entre dans cette livraison : 0020_fraicheur_zone_or.sql.
Elle vient de la #206 et doit s'appliquer au déploiement.

Ce qui change pour la démonstration

  • Le tableau de bord passe de trois à six vues, avec rafraîchissement
    automatique. C'est ce que la démo montrait en version périmée jusqu'ici.
  • Le tableau « conteneurs » de la supervision se remplit à nouveau (#203).
  • La chaîne argent et or tourne au quart d'heure, l'agrégat horaire suit
    (#206).
  • Une heure sans mesure se voit désormais dans les courbes au lieu de se lire
    comme une droite ou comme une consommation nulle (#217).

Relecture

  • Un pair a relu et laissé un commentaire, même court
  • Ses remarques sont traitées, ou une réponse explique pourquoi elles ne le sont pas

Où regarder en priorité

1. La migration. 0020_fraicheur_zone_or.sql est la seule opération non
réversible d'un simple retour de code. À relire avant la fusion, pas après.

2. Le déploiement n'est pas gardé par la chaîne. deploy.yml se déclenche
sur push: main, sans workflow_run ni needs. Sur un exécuteur unique, le
déploiement peut donc partir avant que la chaîne ait rendu son verdict. La
barrière est une convention de fusion, pas un mécanisme. Ce constat est
antérieur à cette livraison et n'a pas de ticket ; il vaut d'être vu ici parce
que c'est la première mise en production depuis qu'il a été écrit.

3. Ce qui reste en réserve après cette livraison : #215, le panneau de
traçabilité qui contredit la courbe, et #205, le délai réel de la prévision.
Les deux sont écrits dans le PRD, aucun ne bloque la démonstration.

4. L'heure. Cette livraison part un soir de fin de sprint. Si quelque
chose casse sur le serveur, le retour arrière est git revert du commit de
fusion puis redéploiement — la migration 0020, elle, ne se défait pas toute
seule.

## Ce que ça change Mise en production de tout ce qui a été fusionné dans `develop` depuis le 07/09 : la refonte du tableau de bord, l'ETL au quart d'heure, la supervision des conteneurs réparée, les manuels d'exploitation, et les trois correctifs front de la soirée. C'est le **rang 1 du backlog** et le critère de sortie ajouté au jalon J2 : `main` = `develop`. Refs #48 ## Preuve ``` 24 fusions, 16 demandes de fusion 135 fichiers, +8 461 / −1 500 ``` Les demandes livrées : ``` #21 #44 #184 #186 #196 #203 #204 #206 #207 #209 #210 #214 #216 #217 #218 #219 ``` Chacune est entrée dans `develop` avec ses quatre tâches de chaîne au vert et l'approbation d'un pair. Aucune n'a été forcée. **Une migration entre dans cette livraison** : `0020_fraicheur_zone_or.sql`. Elle vient de la #206 et doit s'appliquer au déploiement. ## Ce qui change pour la démonstration - Le tableau de bord passe de trois à six vues, avec rafraîchissement automatique. C'est ce que la démo montrait en version périmée jusqu'ici. - Le tableau « conteneurs » de la supervision se remplit à nouveau (#203). - La chaîne argent et or tourne au quart d'heure, l'agrégat horaire suit (#206). - Une heure sans mesure se voit désormais dans les courbes au lieu de se lire comme une droite ou comme une consommation nulle (#217). ## Relecture - [ ] Un pair a relu et laissé un commentaire, même court - [ ] Ses remarques sont traitées, ou une réponse explique pourquoi elles ne le sont pas ## Où regarder en priorité **1. La migration.** `0020_fraicheur_zone_or.sql` est la seule opération non réversible d'un simple retour de code. À relire avant la fusion, pas après. **2. Le déploiement n'est pas gardé par la chaîne.** `deploy.yml` se déclenche sur `push: main`, sans `workflow_run` ni `needs`. Sur un exécuteur unique, le déploiement peut donc partir **avant** que la chaîne ait rendu son verdict. La barrière est une convention de fusion, pas un mécanisme. Ce constat est antérieur à cette livraison et n'a pas de ticket ; il vaut d'être vu ici parce que c'est la première mise en production depuis qu'il a été écrit. **3. Ce qui reste en réserve après cette livraison** : #215, le panneau de traçabilité qui contredit la courbe, et #205, le délai réel de la prévision. Les deux sont écrits dans le PRD, aucun ne bloque la démonstration. **4. L'heure.** Cette livraison part un soir de fin de sprint. Si quelque chose casse sur le serveur, le retour arrière est `git revert` du commit de fusion puis redéploiement — la migration 0020, elle, ne se défait pas toute seule.
Six défauts relevés à la revue, indépendants et sans arbitrage.

Alignement des colonnes chiffrées. « .tableau td » pèse une classe et un
type, « .tableau__num » une classe seule : le sélecteur générique gagnait,
et l'alignement à droite ne s'appliquait jamais dans aucun des trois
tableaux. « :where » pèse zéro, ce qui rend la règle par défaut battable.

Échelle du profil horaire. Le viewBox était figé à 720 px pendant que le
SVG s'affichait en « width: 100% » : sur son conteneur réel de 1 529 px
tout était multiplié par 2,12, et les graduations déclarées à 12 px
arrivaient à 25,5 px, à côté d'un graphique Chart.js qui, lui, respectait
sa taille. Le dessin se fait maintenant à la largeur mesurée, facteur 1.

Débordement horizontal sur mobile. Un <table> ignore « width: 1px » : porté
par le tableau équivalent du profil, « .ev-hors-ecran » laissait 1 128 px
de large dans une fenêtre de 375, et la page entière défilait de côté. La
classe passe sur un conteneur, et gagne « clip-path » et « max-width ».

Navigation sous 900 px. Elle était masquée quand elle ne portait qu'un lien
vers l'écran où l'on se trouvait déjà ; depuis, Qualité est arrivé et le
menu du compte ne porte que Profil et Se déconnecter. Il n'existait donc
plus aucun chemin de /graphiques vers /qualite sur un téléphone. Elle passe
en seconde ligne, en onglets qui tiennent la cible tactile minimale.

Séparateur décimal. Trois composants appelaient « toFixed », qui rend le
point anglais : la disponibilité moyenne du parc s'affichait « 98.5 % » sur
l'écran Parc et « 98,5 % » sur l'écran Qualité. Tous passent par
« formaterPct », qui existait déjà.

Capitalisation. « text-transform: capitalize » capitalise chaque mot, règle
anglaise : elle donnait « Centre De Données » et abîmait « ALR-0002 » en
« Alr-0002 ». Remplacée par « capitaliser » dans le glossaire, qui met la
capitale sur la première lettre et sur elle seule.

Les quatre premiers ne se voient pas dans un rendu de test — les styles
d'un composant Vue ne sont pas appliqués sous Vitest. « revue-visuelle.test.js »
les prouve sur la source, comme « paletteSeries.test.js » le fait déjà pour
la palette ; chacun a été vu rougir sur le code d'avant.

Douze fichiers repassent au format du dépôt (prettier --no-semi
--single-quote --print-width 100), qui corrige au passage une indentation
ayant dérivé dans TableauQualite.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VQz79FiyjK3wbB1C1qhuTy
docs: désigne les quatre gestes sensibles et pose le journal des rejeux (#44)
All checks were successful
Intégration / Tableau de bord — dépendances, tests et construction (pull_request) Successful in 37s
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 19s
Intégration / Python — qualité, tests et dépendances (pull_request) Successful in 5m25s
164435f933
L'exigence ENF-16 ne porte que sur quatre des quinze manuels du dossier, et
rien ne disait lesquels. Le README les nomme, dit qui tient chaque geste, et
impose les deux sections qu'eux seuls doivent tenir : « Quand ça échoue » et
« Journal des rejeux ».

Le journal est posé vide dans deploiement.md et collecteur.md. Dans reprise.md,
c'est l'ancienne section « Exercice chronométré » qui le devient, avec une
colonne de plus : l'exercice du 02/09 a été joué par l'auteur du manuel, ce qui
ne prouve pas ce que l'exigence demande. La colonne le dit plutôt que de le
laisser deviner.

Cadrage seulement : le contenu de chaque manuel reste écrit par celui qui tient
le geste.
Merge branch 'develop' into gabriel/44-manuels-exploitation
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 7s
Intégration / Workflows — lint et audit de sécurité (pull_request) Successful in 19s
Intégration / Python — qualité, tests et dépendances (pull_request) Successful in 5m37s
f0000e6f25
L'écran Parc portait deux métiers de rythmes différents : savoir où en est
le parc à cette heure-ci, et comparer les sites sur la durée. D'où deux
sélecteurs de période qui s'ignoraient sur la même page, et 2 064 px de
haut. Ils partent chacun sur leur écran, avec une seule période en tête.

/comparaison porte le graphique multi-sites et son sélecteur. Sa période
commence à 7 j : sept sites sur vingt-quatre heures font 168 barres
groupées, que personne ne lit. Le sélecteur quitte la colonne latérale de
280 px, dont le défilement interne ne montrait que quatre sites sur sept —
une légende dont on ne voit pas la moitié ne remplit pas son office ; il
devient une rangée de jetons, et la pleine largeur revient au graphique.

/graphiques garde la surveillance et gagne ce qui lui manquait :

Le bandeau remplace les quatre pavés. Ils avaient chacun un fond, une ombre
et la hauteur du plus grand : celui des alertes portait quatre lignes, les
trois autres une seule, et finissaient sur 77 px de vide. Des filets
verticaux séparent aussi bien, et chaque mesure porte désormais un second
fait — part de la capacité, référence de persistance, sites mesurés — ce
qui lui donne sa hauteur pour de bon.

« À traiter » remonte les sites en surcharge, en tension ou sans relevé.
Il fallait jusqu'ici parcourir une colonne pour les premiers et changer
d'écran pour les seconds, alors que c'est la question que l'accueil devrait
traiter en premier. Aucun appel de plus : tout vient de /v1/sites.

Le tableau porte le taux de charge en jauge graduée aux trois seuils du
glossaire. models.py le dit de lui-même — « la seule grandeur comparable
entre sites » — et il n'était affiché nulle part : au lecteur de diviser
733 par 800 de tête. Il porte aussi une micro-courbe par site, trie du plus
chargé au moins chargé, et liste tout le parc plutôt que les sites cochés,
puisqu'il n'y a plus rien à cocher ici.

Chart.js écrivait en Helvetica faute de « Chart.defaults.font.family » :
deux typographies par écran, dont une que personne n'avait choisie. Les
réglages communs vivent dans chartReglages.js, et l'axe passe de dix
graduations à cinq.

Le contenu est enfin borné (--ev-contenu-max) : il s'étirait à 1 609 px sur
un écran de 1 920, et les colonnes devenaient des couloirs vides.

Rendu vérifié dans Chrome sur le jeu de fixtures, en 1 920 et en 375 :
l'écran d'accueil passe de 2 064 px à 1 311, sans débordement horizontal.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Le contenu était borné mais calé à gauche : tout le blanc s'accumulait
contre le bord droit de la fenêtre. « margin-inline: auto » le répartit, et
la règle vaut pour les six écrans — les quatre qui n'étaient pas encore
bornés le sont aussi.

Au passage, un défaut vu en vrai derrière la modale de reprise : la page
annonçait « Aucun site dans le parc » alors que les sites existent et que
c'est la session qui est tombée. Qui ferme la modale, ou la lit de biais,
repartait avec une information fausse sur son parc. Trois écrans le
faisaient — Parc, Comparer, et les deux blocs de Qualité, qui disaient
« Aucun site visible » et « Aucune alerte sur les sites visibles ».

Un parc vide se dit, une session finie se tait : c'est la règle que
GraphiquesView portait déjà en commentaire pour ses messages d'erreur, sans
que l'état vide la suive.

Le test correspondant signale lui-même l'expiration au dépôt de session :
son double de « listerSites » court-circuite la couche API, qui est
l'endroit où un 401 le fait en vrai. Vu rougir sans le correctif.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Merge branch 'develop' into gabriel/44-manuels-exploitation
All checks were successful
Intégration / Contrôles statiques du dépôt (pull_request) Successful in 8s
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 39s
Intégration / Python — qualité, tests et dépendances (pull_request) Successful in 5m56s
09b67c344b
Conflit sur `docs/runbooks/collecteur.md` : develop y a posé le rejeu de
Justine (#44) avec un tableau à quatre colonnes, la branche y posait le
journal vide au format que le README rend obligatoire.

Résolution : le format du README l'emporte (cinq colonnes, dont « auteur du
manuel ? »), et le rejeu de Justine s'y écrit — « non » à la colonne qui
compte, puisqu'elle n'est pas l'auteur du manuel. C'est la première ligne de
journal qui mesure le manuel et pas la machine.

Deux mises à jour que le merge ne pouvait pas voir : `reentrainement.md`
existe depuis le #187, donc le README le lie au lieu de l'annoncer « à
écrire », et la liste des manuels restant à écrire se réduit à Keycloak.
docs: le manuel de réentraînement porte le journal des rejeux (#44)
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 19s
Intégration / Python — qualité, tests et dépendances (pull_request) Successful in 6m2s
277f17b365
Merge branch 'develop' into gabriel/44-manuels-exploitation
All checks were successful
Intégration / Tableau de bord — dépendances, tests et construction (pull_request) Successful in 41s
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 27s
Intégration / Python — qualité, tests et dépendances (pull_request) Successful in 5m33s
0d0169256c
Conflit sur reprise.md : develop y a posé « En cas de panne » et
« Retour arrière », la branche renommait « Exercice chronométré » en
« Journal des rejeux ». Les deux sont gardés.

Deux suites du conflit, invisibles pour git :
- le README annonçait « ## Quand ça échoue » alors que les manuels
  arrivés par develop nomment la section « En cas de panne » (comme le
  gabarit mlflow.md §8) : README et reentrainement.md alignés dessus ;
- la ligne du 02/09 du journal des rejeux donnait comme cause un
  contournement que « En cas de panne » démontre inopérant : elle porte
  maintenant le vrai diagnostic (mode « restoring »).
Merge branch 'develop' into gabriel/44-manuels-exploitation
All checks were successful
Intégration / Tableau de bord — dépendances, tests et construction (pull_request) Successful in 42s
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 23s
Intégration / Python — qualité, tests et dépendances (pull_request) Successful in 6m21s
e6ead264a0
supervision : le tableau de bord des conteneurs ne voyait plus un seul conteneur (#203)
Some checks failed
Intégration / Tableau de bord — dépendances, tests et construction (pull_request) Successful in 44s
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 19s
Intégration / Python — qualité, tests et dépendances (pull_request) Failing after 1m24s
af05298f62
Panne silencieuse : la cible `cadvisor` était `up`, le conteneur `healthy`,
et il publiait bien 2 500 séries — mais toutes portaient `id="/"`, le cgroup
racine. Aucune ne portait de nom de conteneur, donc les requêtes en
`{name!=""}` des tableaux de bord « conteneurs » et « socle » ne rendaient
rien : quatre stats à zéro et « No data » partout.

La cause n'est pas dans le dépôt. Le démon de ml-stagiaire-02 est passé au
containerd image store (Docker 29 : `Storage Driver: overlayfs`,
`io.containerd.snapshotter.v1`) : la couche d'écriture d'un conteneur n'est
plus sous /var/lib/docker/image/, cAdvisor v0.52 ne la trouve pas, et il
écarte chaque conteneur en entier plutôt que de publier ce qu'il en sait.
Son journal le disait, une ligne par conteneur et par minute.

Deux pistes essayées et écartées : monter en version — v0.52.1 est la
dernière publiée — et couper les métriques de disque, qui ne change rien.
Reste la lecture directe de containerd, dans le namespace `moby` où Docker
range ses conteneurs. Son handler Docker doit être neutralisé au passage,
sans quoi il reprend la main sur les mêmes cgroups pour les rejeter.

Ce que ça coûte : containerd ne connaît pas les noms Docker, `name` devient
un identifiant de 64 caractères. D'où `noms-conteneurs`, qui publie la
correspondance identifiant -> nom lisible que les panneaux joignent par
`on(name) group_left(nom)`. Même parti que le relais d'alertes : un fichier
de bibliothèque standard monté dans l'image Python officielle, écoute bornée
à la boucle locale. Il parle au démon Docker et en a donc tous les pouvoirs,
mais n'appelle qu'une route, en lecture.

Le banc tient désormais le remède en place : la lecture de containerd, la
présence du service, le job de collecte, et le fait qu'aucun panneau ne
regroupe plus par (name). Aucun contrôle n'aurait attrapé la panne à
l'exécution — c'est ce que le manuel documente maintenant, avec le contrôle
qui la révèle en une commande.

Les panneaux réseau par conteneur restent presque vides, et c'est attendu :
nos piles tournent en réseau hôte et n'ont pas de compteurs réseau propres.
Leur description le dit désormais au lecteur.
supervision : noms.py passe le lint du dépôt (#203)
All checks were successful
Intégration / Tableau de bord — dépendances, tests et construction (pull_request) Successful in 41s
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 17s
Intégration / Python — qualité, tests et dépendances (pull_request) Successful in 5m32s
bbee59f332
`ruff check infra` couvre le répertoire : shebang sans bit d'exécution,
concaténation implicite dans une liste, `.format()` au lieu d'une f-chaîne,
`sorted()[0]` pour un minimum. Aucun changement de comportement.

Un mot de plus au passage sur le choix du nom : un conteneur peut porter
plusieurs alias, et prendre le premier venu ferait changer la série de nom
d'un scrape à l'autre — les courbes se couperaient en deux sans que rien
ne l'explique.
Merge pull request 'docs : les quatre gestes sensibles d'ENF-16 sont nommés, et un rejeu a où s'écrire (#44)' (#186) from gabriel/44-manuels-exploitation 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 38s
Intégration / Workflows — lint et audit de sécurité (push) Successful in 28s
Intégration / Python — qualité, tests et dépendances (push) Successful in 5m46s
0727fd187a
Reviewed-on: https://10.105.200.41/g2/enervision/pulls/186
Reviewed-by: marvin <marvin@noreply.10.105.200.41>
Reviewed-by: lenaic <lenaic@noreply.10.105.200.41>
Reviewed-by: olivier <olivier@noreply.10.105.200.41>
Merge branch 'develop' into gabriel/203-conteneurs-invisibles
All checks were successful
Intégration / Tableau de bord — dépendances, tests et construction (pull_request) Successful in 42s
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 20s
Intégration / Python — qualité, tests et dépendances (pull_request) Successful in 5m48s
2d951b5f15
etl: la chaine passe au quart d'heure et l'agregat suit (#205)
Some checks failed
Intégration / Tableau de bord — dépendances, tests et construction (pull_request) Successful in 42s
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) Failing after 19s
Infra Ansible / Playbooks Ansible valides (pull_request) Failing after 3m0s
Intégration / Python — qualité, tests et dépendances (pull_request) Successful in 5m50s
51fa3d7e04
La prevision annoncee H+1 travaillait a trois heures d'ecart. Mesure en
production le 08/09 a 12 h 58 : dernier seau de mesure_horaire a 10 h 00,
pendant que la passe de 12 h 35 visait 13 h 00. La colonne retard_1h_kw du
modele contenait donc la moyenne de 10 h pour predire 13 h.

Deux reglages poses separement s'additionnaient sans que personne ne les
regarde ensemble. Aucun des deux n'etait faux seul.

La chaine ETL tournait une fois par heure : les minutes de l'heure H
n'arrivaient dans public.mesure qu'a (H+1):27. La 0016 gardait donc un
end_offset d'une heure sur mesure_horaire, pour ne pas materialiser un seau
nourri de lignes pas encore chargees. Son commentaire dit l'intention, elle
est juste ; le moyen etait plus fort que necessaire. Un seau se materialise
quand il tient entierement dans la fenetre, donc le seau H, clos a (H+1):00,
attendait le passage de (H+2):00.

La chaine passe au quart d'heure : argent a :00 :15 :30 :45, or trois minutes
derriere, chargement cinq de plus. L'heure H est complete en base a (H+1):08
au pire. La migration 0020 ramene alors end_offset et schedule_interval a
quinze minutes, et le seau H devient lisible a (H+1):15. L'ordre des deux
changements n'est pas indifferent : toucher l'agregat en premier
materialiserait des seaux a partir de lignes pas encore chargees.

Verifie avant d'ecrire la migration, parce que la crainte de la 0016 est
legitime. Sur enervision_preprod, une ligne temoin posee dans l'heure EN COURS
puis un refresh sur une fenetre finissant trente minutes A L'INTERIEUR de ce
seau : le seau incomplet n'est pas materialise, TimescaleDB tronque la fenetre
a la borne inferieure. La preproduction a ete remise a l'identique.

La migration n'appelle pas refresh_continuous_aggregate. Il refuse de tourner
dans une transaction et run_migrations applique chaque fichier dans une
transaction : l'ajouter faisait echouer le demarrage de l'API, donc le
deploiement. Le manuel d'exploitation dit comment le forcer a la main et
previent qu'un controle joue dans la minute qui suit verra encore l'ancien
retard.

Le plancher reste de deux seaux d'ecart, pas un. Une prevision emise avant le
debut de son heure cible ne peut pas utiliser l'heure qui la precede, encore
en train de se remplir. Descendre a un seul exigerait d'emettre apres le debut
de l'heure visee, donc d'echanger l'avance contre la precision : arbitrage
EC06, pose dans le ticket et non tranche ici.

La prevision reste horaire, EF-07 en demande une par heure et pas quatre. Elle
gagne au passage douze minutes de marge derriere le chargement de :23, contre
huit derriere celui de :27.
« À traiter » s'étirait sur toute la hauteur de sa rangée dès que
plusieurs sites décrochaient, poussant le tableau des sites hors de
l'écran. La liste montre trois lignes puis défile ; le compte en tête
dit le total.

« Alertes » sur l'écran Qualité rendait la liste entière : un mauvais
jour, le pied de page partait sous la ligne de flottaison. Vingt par
page, les plus sévères d'abord (elles sont déjà triées en tête), avec
« Précédentes / Suivantes » et l'intervalle affiché. Le découpage est
fait côté client — l'API rend tout, sans paramètre de page.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EL8Yw5fQMitWWyKR4FugX3
ci: le banc de fraicheur passe shellcheck (#205)
Some checks failed
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 24s
Intégration / Tableau de bord — dépendances, tests et construction (pull_request) Successful in 41s
Infra Ansible / Playbooks Ansible valides (pull_request) Failing after 1m56s
Intégration / Python — qualité, tests et dépendances (pull_request) Successful in 5m45s
9bf76b5faa
Deux defauts reels, releves par la chaine et non par moi.

SC1087, une ERREUR : « $reglage[[:space:]] » se lit comme une expansion de
tableau. Le motif aurait pu devenir muet sans que rien ne le signale, sur le
controle qui verifie justement que la 0020 tient ses quinze minutes. Accolades
posees.

SC2317 : « derniere_minute » etait definie et jamais appelee. Le calcul se fait
sur place la ou il sert. Retiree plutot que gardee au cas ou.

Verifie avec la meme version que la chaine, koalaman/shellcheck:v0.9.0, sur le
banc et sur les onze autres : aucun n'a de reproche.
dashboard: les alertes passent sur leur propre écran (#184)
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 26s
Intégration / Tableau de bord — dépendances, tests et construction (pull_request) Successful in 40s
Infra Ansible / Playbooks Ansible valides (pull_request) Successful in 1m46s
Intégration / Python — qualité, tests et dépendances (pull_request) Successful in 5m8s
4ee28e6cbd
Elles vivaient en pied de l'écran Qualité, sous la synthèse et le
tableau par site. Deux défauts à cet endroit : la liste, paginée à
vingt, poussait le reste de la page ; et une alerte est une chose à
traiter, pas un indicateur de qualité de la collecte — elle ne
partageait le domaine que par accident de rangement.

Nouvel onglet « Alertes » (/alertes), quatrième de la barre, avec sa
propre coquille. Il charge le référentiel et les alertes côte à côte —
`ListeAlertes` a besoin du premier pour nommer le site de chaque
alerte. `charger()` de l'écran Qualité n'appelle plus `/v1/alertes` ;
le « Réessayer » de la liste recharge les alertes seules.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EL8Yw5fQMitWWyKR4FugX3
ci: vars.yml repasse ansible-lint, une ligne de 103 caracteres (#205)
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 24s
Intégration / Tableau de bord — dépendances, tests et construction (pull_request) Successful in 37s
Infra Ansible / Playbooks Ansible valides (pull_request) Successful in 1m35s
Intégration / Python — qualité, tests et dépendances (pull_request) Successful in 5m43s
e7597b742b
Le job « Playbooks Ansible valides » sort rouge sur cette branche, et pas sur
le fond du ticket :

  yaml[line-length] group_vars/all/vars.yml:199 -- Line too long
  (103 > 100 characters)

La prose ajoutee sur la mesure des marges a laisse la fin de la phrase
d'origine collee derriere elle, « Aucune des deux ne choisit sa journee --
elles prennent la ». Le paragraphe est replie, le texte est mot pour mot le
meme.

Le niveau « warning » de infra/ansible/.yamllint ne protege pas ici : le
profil moderate d'ansible-lint rend la regle fatale, et c'est bien « 1
violation(s) that are fatal » que le journal annonce.

Verifie : plus aucune ligne au-dela de 100 caracteres dans infra/ansible,
yamllint propre sur les deux arbres, shellcheck propre sur .forgejo/scripts et
tests/ci, et le banc de fraicheur au vert -- toujours rouge, aussi, quand on
remet end_offset a une heure.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Merge pull request 'etl : la chaîne passe au quart d'heure et l'agrégat suit' (#206) from lenaic/205-fraicheur-zone-or into develop
Some checks failed
Intégration / Contrôles statiques du dépôt (push) Successful in 9s
Intégration / Workflows — lint et audit de sécurité (push) Successful in 23s
Intégration / Tableau de bord — dépendances, tests et construction (push) Successful in 37s
Infra Ansible / Playbooks Ansible valides (push) Successful in 1m11s
Intégration / Python — qualité, tests et dépendances (push) Has been cancelled
10cc241e4a
Reviewed-on: https://10.105.200.41/g2/enervision/pulls/206
Reviewed-by: olivier <olivier@noreply.10.105.200.41>
Merge pull request 'supervision : le tableau de bord des conteneurs ne voyait plus un seul conteneur (#203)' (#204) from gabriel/203-conteneurs-invisibles into develop
All checks were successful
Intégration / Contrôles statiques du dépôt (push) Successful in 7s
Intégration / Workflows — lint et audit de sécurité (push) Successful in 22s
Intégration / Tableau de bord — dépendances, tests et construction (push) Successful in 37s
Intégration / Python — qualité, tests et dépendances (push) Successful in 5m28s
27a552fa51
Reviewed-on: https://10.105.200.41/g2/enervision/pulls/204
Reviewed-by: lenaic <lenaic@noreply.10.105.200.41>
dashboard: les écrans de surveillance se rafraîchissent seuls (#196)
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 23s
Intégration / Tableau de bord — dépendances, tests et construction (pull_request) Successful in 42s
Intégration / Python — qualité, tests et dépendances (pull_request) Successful in 5m27s
a4f9655dc0
Les vues chargeaient sur `onMounted` et rien ne repartait : un écran
laissé ouvert affichait l'état de l'instant où on l'avait ouvert, sans
le dire — et d'abord sur l'heure, `derniere_releve` portant l'heure
réelle depuis le #179.

`surRafraichissement` rejoue les appels de l'écran toutes les minutes,
tant que l'onglet est au premier plan (`document.visibilityState`), et
une fois de plus dès qu'il y revient. Parc, Site, Qualité et Alertes
gagnent chacun un `rafraichir()` qui rejoue sans vider : un bloc déjà
rempli garde sa valeur si l'appel échoue, et `EtatRafraichissement`
dit l'heure du dernier cycle réussi, ou que la donnée a vieilli.

Les profondeurs d'affichage, recopiées dans deux vues, passent dans
`src/periodes.js` — une seule liste.

Hors périmètre (le ticket le pose) : l'écran Comparer, qui porte des
journées closes, et le temps réel poussé par le serveur.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EL8Yw5fQMitWWyKR4FugX3
dashboard: la période de tête commande vraiment le profil et les séries (#184)
All checks were successful
Intégration / Tableau de bord — dépendances, tests et construction (pull_request) Successful in 42s
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 18s
Infra Ansible / Playbooks Ansible valides (pull_request) Successful in 1m35s
Intégration / Python — qualité, tests et dépendances (pull_request) Successful in 5m15s
f104dd0f43
Deux défauts relevés en relecture de la #207, tous deux sur la période que
cette PR met en tête de chaque écran.

Les séries survivaient au changement d'écran. Chaque vue pose sa fenêtre puis
recharge le référentiel, qui ne relançait une série qu'en l'absence d'entrée :
les points 24 h de l'écran Parc restaient donc en place sur /comparaison, sous
une étiquette « 7 j », et réciproquement les micro-courbes du tableau du parc
montraient sept jours sous un en-tête « 24 h ». Chaque entrée de `series`
retient maintenant la fenêtre pour laquelle elle a été demandée, et c'est cette
marque — non la simple présence de l'entrée — qui décide du rechargement.

ProfilHoraireParc restait écrit pour 24 h au pas horaire. Il pouvait l'être
tant que sa courbe était figée à 24 h ; depuis qu'elle suit le sélecteur, un
point journalier passé à `etiquetteAxe(t, '1h')` rendait l'heure de son seau,
soit « 00 h » répété sur les sept ou trente points, sous un titre qui annonçait
« 24 h » et un tableau dont la colonne s'intitulait « Heure (UTC) ». Le titre,
les étiquettes, le résumé et l'en-tête se règlent désormais sur `pas` et
`fenetre` rendus par l'API. L'espacement des étiquettes se calcule au lieu
d'être constant : quatre convenait aux vingt-quatre heures et à elles seules.

Le gabarit de série des tests portait « fenetre: '7j' » quand le magasin
démarre à « 24h » — corrigé pour refléter ce que rend l'API.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Passe de vérification de bout en bout : chaque affirmation vérifiable a été
confrontée au dépôt, au serveur ou aux deux. Les corrections portent sur des
écarts entre ce qui est écrit et ce qui tourne, pas sur la forme.

Principaux écarts fermés :

- ADR 0001 : l'exception PostgreSQL était levée depuis le 02/09, la
  composition est par pile, les secrets sont en 0640 et non 0600
- ADR 0002 : SameSite réel (Lax sur ev_access, Strict sur ev_refresh),
  condition de bascule Keycloak échue et non remplie
- ADR 0005 et CONVENTIONS : endpoint=alerts manquait, hour= absent des clés
  /readings, la zone or écrit table=mesure et non mesure_horaire
- ADR 0006 : le report se borne sur l'âge de la valeur, pas sur la longueur
  du trou ; l'imputation ne touche que les deux grandeurs décisionnelles
- ADR 0012 et plan de migration : le conteneur d'archive est déclaré mais
  jamais appliqué — l'état distant ne porte que la source de données
- docs/api : les dix routes /v1 n'étaient documentées nulle part, le
  filtrage par site est inerte, le tableau de bord sert des fixtures
- runbooks : codes de sortie du collecteur inversés (all et non any),
  chemins Ruff incomplets, zones sensibles de la CI fausses

Un bug corrigé au passage : restore.yml testait la sortie de pg_isready
contre une chaîne anglaise alors que le serveur répond en français. La
tâche échouait donc après avoir restauré les bases. Le verdict passe au
code de sortie.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01D9QRxZiZ6TZzSAmtTsg6Sw
# Conflicts:
#	docs/runbooks/README.md
#	docs/runbooks/deploiement.md
#	docs/runbooks/etl.md
Trois constats écrits ce matin sont périmés depuis la fusion de develop :

- « Le #37 n'est pas commencé » : le paquet services/inference/inference/
  existe, prevoir.sh tourne sous cron à la minute 35, public.prevision porte
  21 lignes dont la dernière pour 14 h 00 UTC, et fixtures.py n'invente plus
  de prévision. Le document affirmait le contraire deux sections avant
  « Ce que le #37 a tranché ».
- La fenêtre d'évaluation par défaut n'est plus de trois jours mais de
  quatorze (_JOURS_TEST_DEFAUT), portée par le #36 le 08/09.
- L'état « proposée » était dit deux fois, la seconde plus précisément :
  le paragraphe de clôture porte la condition de levée.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01D9QRxZiZ6TZzSAmtTsg6Sw
auth : un utilisateur change son propre mot de passe (#21)
All checks were successful
Intégration / Tableau de bord — dépendances, tests et construction (pull_request) Successful in 41s
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 1m28s
Intégration / Python — qualité, tests et dépendances (pull_request) Successful in 5m34s
a38bceea24
La page de profil était un stub : elle affichait l'identité et rien de
plus, alors que le critère d'acceptation du ticket est le changement de
mot de passe. Aucun endpoint d'auto-service n'existait — seul
`PATCH /auth/users/{id}`, réservé aux admins et sans vérification du
mot de passe actuel.

API — `POST /auth/me/password` (`{current_password, new_password}`,
authentifié) : vérifie le mot de passe actuel (un jeton d'accès volé ne
suffit pas), le remplace, coupe toutes les sessions de l'utilisateur,
puis réémet une session pour l'appelant — ses cookies remplacent les
anciens dans la réponse. 403 si l'actuel est faux, 422 si le nouveau
est trop court ou identique. Journalisé : `password_changed` /
`password_change_ko`.

Dashboard — `MotDePasseFormulaire`, monté sur la page de profil :
actuel + nouveau + confirmation, la confirmation vérifiée côté client.
Succès : champs vidés, « vos autres appareils ont été déconnectés ».

Docs d'auth mises à jour (endpoints, codes, décisions).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EL8Yw5fQMitWWyKR4FugX3
révision : sort restore.yml et autorisation.py du périmètre documentaire
All checks were successful
Intégration / Tableau de bord — dépendances, tests et construction (pull_request) Successful in 37s
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 19s
Intégration / Python — qualité, tests et dépendances (pull_request) Successful in 5m56s
9da8cefe86
Ces deux fichiers sont du code, pas de la documentation. Ils reviennent à
l'identique de develop pour que cette branche ne porte que des documents.

Les deux constats qui avaient motivé les changements restent valables et
sont à traiter dans leur propre ticket :

- infra/ansible/restore.yml teste la sortie de pg_isready contre la chaîne
  anglaise « accepting connections » alors que le serveur répond en
  français, « acceptation des connexions ». La condition failed_when est
  donc vraie à tous les coups : le playbook rougit APRÈS avoir restauré les
  bases. Vérifié le 08/09 sur ml-stagiaire-02. Le correctif tient en une
  ligne — s'appuyer sur le code de sortie de pg_isready, qui vaut 0 quand le
  serveur accepte. Le piège reste documenté dans docs/runbooks/reprise.md.

- dashboard/autorisation.py rend tous les sites sans lire ops.acces_site :
  tout compte authentifié voit les sept, et l'ENF-02 n'est pas tenue. Le
  docstring du module reste celui de develop. L'écart est documenté dans
  docs/api/decisions.md, authentification.md et l'ADR 0002.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01D9QRxZiZ6TZzSAmtTsg6Sw
Fusionne develop : la refonte du tableau de bord 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 23s
Intégration / Tableau de bord — dépendances, tests et construction (pull_request) Successful in 41s
Intégration / Python — qualité, tests et dépendances (pull_request) Successful in 5m50s
de29d556d3
Cinq conflits, tous sur le tableau de bord, tous de la même forme : develop a
ajouté un comportement là où la #184 avait renommé ou refait la structure. Rien
n'a été arbitré au profit d'un seul côté — le comportement de develop est
reporté dans la structure de la #184.

- `glossaire.js` : les quatre régimes d'imputation de develop (#199, `measured`
  la mesure et `none` le trou assumé) dans la forme d'écriture de la #184.
  `capitaliser` reste.
- `FicheSite.vue` : les mentions de qualité et de dernière valeur connue (#199)
  et le garde sur l'unité d'une marge nulle, avec la capitalisation française
  et `formaterPct` de la #184.
- `GraphiqueSite.vue` : la prévision absente du #37 garde sa structure, l'écart
  garde le formateur français — c'est-à-dire une virgule décimale, ce que
  `toFixed(1)` ne donnait pas.
- `SyntheseParc.vue` : la #184 a renommé « .pavé* » en « .mesure* » et refait le
  bandeau ; les deux faits que develop y avait ajoutés sont reportés dans la
  nouvelle nomenclature — la mention d'un total partiel (#199) et les deux
  absences distinctes de la prévision (#37).
- `TableauSites.vue` : seuls les imports divergeaient ; les mentions du #199 se
  sont posées d'elles-mêmes dans la colonne « Conso. » refaite.

Un piège au passage, dans le report de la mention de total partiel :
`.mesure__second--partiel` et `.mesure__second` pèsent une classe chacune, donc
c'est l'ordre qui tranche. Posée avant, la teinte de vigilance était écrasée par
l'encre secondaire et rien ne distinguait plus un total partiel — exactement la
collision de spécificité que cette PR corrige sur `.tableau__num`. La règle
passe après, et `revue-visuelle.test.js` le retient.

Trois cas ajoutés : la mention de total partiel n'avait aucun test de son côté,
et elle vient de changer de nom.

425 tests verts, `npm run build` ok.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Fusionne develop : la refonte du tableau de bord rencontre les correctifs #199, #179 et #37
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 41s
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
0b6628449f
La branche datait du 58f4c70 et develop avait quatre-vingt-dix commits
d'avance. Cinq fichiers du tableau de bord se sont croisés : le #184 les a
reformatés et renommés, develop y a ajouté du comportement. La règle tenue
partout est la même — aucun comportement présent sur develop ne disparaît,
la forme est celle de la refonte.

- `api/glossaire.js` : la table des méthodes est celle de develop, à quatre
  régimes. `measured` est la mesure et `none` le trou assumé depuis la
  migration 0010 ; la forme à trois entrées de la branche les confondait, et
  l'écran Qualité affichait « measured » en anglais. La branche n'y ajoute
  que `capitaliser`.

- `components/FicheSite.vue` : listes d'imports réunies, et les trois
  mentions du #199 et du #179 conservées — la dernière valeur connue à côté
  du tiret, l'état de la valeur reconstituée, « rien mesuré à cette minute ».
  La marge garde son `kW` conditionnel : « — kW » se lit comme zéro.

- `components/TableauSites.vue` : imports réunis. `libellePalier` ne sert
  nulle part dans ce fichier, il ne rentre pas.

- `components/GraphiqueSite.vue` : la structure de develop — un site sans
  prévision le dit, au lieu d'afficher « 0 kW » — avec le `formaterPct` du
  #184 à la place de `toFixed(1)`, qui rendait le point décimal anglais.

- `components/SyntheseParc.vue` : le bandeau du #184 accueille les trois
  cas de develop (écart servi, aucune prévision servie, prévision sans
  référence), l'unité conditionnelle de la prévision, et la mention de
  total partiel du #199, renommée `mesure__second--partiel` avec le reste
  du bandeau. Elle reste sous le total qu'elle qualifie : la disponibilité
  dit déjà « x/y mesurés », mais c'est le nombre de kilowatts qu'on risque
  de lire comme une baisse.

Vérifié sur l'arbre fusionné : `npm run test:unit` → 448 tests, 41 fichiers,
tous verts ; `npm run build` avec `VITE_API_BASE` → ok. Aucun fichier de test
de develop n'a été perdu.
Merge pull request 'dashboard : surveiller, comparer et alerter sur trois ecrans (#184)' (#207) from marvin/184-refonte-tableau-de-bord into develop
All checks were successful
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 7s
Intégration / Workflows — lint et audit de sécurité (push) Successful in 19s
Intégration / Python — qualité, tests et dépendances (push) Successful in 6m8s
5797f13b0a
Reviewed-on: https://10.105.200.41/g2/enervision/pulls/207
Reviewed-by: olivier <olivier@noreply.10.105.200.41>
Fusionne develop : la #207 est passée, la branche se réduit à ses 21 fichiers
Some checks failed
Intégration / Tableau de bord — dépendances, tests et construction (pull_request) Successful in 45s
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 17s
Intégration / Python — qualité, tests et dépendances (pull_request) Has been cancelled
77642ed021
La #207 fusionnée, `develop` porte désormais la refonte du #184 — et sa
propre résolution des conflits avec la zone or (de29d55), faite en parallèle
de la mienne du 0b66284. Là où les deux disent la même chose autrement, c'est
`develop` qui est repris mot pour mot : deux rédactions du même arbitrage ne
valent pas un conflit de plus au prochain tour.

- `api/glossaire.js`, `components/FicheSite.vue`, `components/SyntheseParc.vue` :
  identiques à `develop`, à la ligne près. Le #196 ne touche aucun de ces
  fichiers ; il n'avait pas à y laisser de trace.

- `stores/graphique.js` : le seul arbitrage réel. Le #184 fait porter à chaque
  entrée de série la fenêtre pour laquelle elle a été demandée (`_serieAJour`),
  le #196 ajoute le rafraîchissement doux. Les deux se composent :
  `_chargerSerie(siteId, { doux })` pose `fenetre: demandee` comme sur
  `develop`, et `garderAffiche` exige maintenant la fenêtre courante en plus du
  statut « pret ». Sans cette condition, un rafraîchissement en 24 h aurait
  gardé à l'écran une courbe 7 j — le défaut même que le #184 vient de
  corriger.

Vérifié sur l'arbre fusionné : `npm run test:unit` → 455 tests, 41 fichiers,
tous verts ; `npm run build` avec `VITE_API_BASE` → ok. Le diff avec `develop`
est exactement les 21 fichiers du #196, plus rien.
Merge branch 'develop' into marvin/21-changer-mot-de-passe
Some checks failed
Intégration / Tableau de bord — dépendances, tests et construction (pull_request) Successful in 41s
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
Intégration / Python — qualité, tests et dépendances (pull_request) Has been cancelled
2bf4231e2b
Merge branch 'develop' into florian/maj-docs
Some checks failed
Intégration / Tableau de bord — dépendances, tests et construction (pull_request) Successful in 44s
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 18s
Intégration / Python — qualité, tests et dépendances (pull_request) Has been cancelled
e9f7ef0e17
Merge pull request 'dashboard : les ecrans de surveillance se rafraichissent seuls (#196)' (#209) from marvin/196-rafraichissement-tableau-de-bord into develop
Some checks failed
Intégration / Contrôles statiques du dépôt (push) Waiting to run
Intégration / Workflows — lint et audit de sécurité (push) Waiting to run
Intégration / Tableau de bord — dépendances, tests et construction (push) Has been cancelled
Intégration / Python — qualité, tests et dépendances (push) Has been cancelled
7b8e914982
Reviewed-on: https://10.105.200.41/g2/enervision/pulls/209
Reviewed-by: olivier <olivier@noreply.10.105.200.41>
Merge branch 'develop' into marvin/21-changer-mot-de-passe
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
4d4dd273c3
Merge pull request 'auth : un utilisateur change son propre mot de passe (#21)' (#210) from marvin/21-changer-mot-de-passe into develop
All checks were successful
Intégration / Contrôles statiques du dépôt (push) Successful in 9s
Intégration / Tableau de bord — dépendances, tests et construction (push) Successful in 46s
Intégration / Workflows — lint et audit de sécurité (push) Successful in 20s
Intégration / Python — qualité, tests et dépendances (push) Successful in 5m38s
a9af37ed7c
Reviewed-on: https://10.105.200.41/g2/enervision/pulls/210
Reviewed-by: gabriel <gabriel@noreply.10.105.200.41>
Correction docs
Some checks failed
Intégration / Contrôles statiques du dépôt (pull_request) Has been cancelled
Intégration / Python — qualité, tests et dépendances (pull_request) Has been cancelled
Intégration / Workflows — lint et audit de sécurité (pull_request) Has been cancelled
Intégration / Tableau de bord — dépendances, tests et construction (pull_request) Has been cancelled
b0694d7978
Merge branch 'develop' into florian/maj-docs
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 23s
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 5m43s
c3de21e41d
Résout le conflit sur docs/api/authentification.md : conserve à la
fois la précision develop sur le 403 (mot de passe actuel incorrect)
et l'ajout florian sur le 404 (site inconnu ou non autorisé).
Merge pull request 'florian/maj-docs' (#214) from florian/maj-docs into develop
All checks were successful
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 21s
Intégration / Tableau de bord — dépendances, tests et construction (push) Successful in 40s
Intégration / Python — qualité, tests et dépendances (push) Successful in 5m29s
51bd956167
Reviewed-on: https://10.105.200.41/g2/enervision/pulls/214
Reviewed-by: marvin <marvin@noreply.10.105.200.41>
Reviewed-by: gabriel <gabriel@noreply.10.105.200.41>
docs: journal J6 et J7, PRD 1.2 et backlog réalignés sur la forge au 08/09 (#48)
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 21s
Intégration / Tableau de bord — dépendances, tests et construction (pull_request) Successful in 42s
Intégration / Python — qualité, tests et dépendances (pull_request) Successful in 5m33s
43a3991c7c
Le PRD et le backlog décrivaient l'état du 4 septembre. Entre les deux, 50
demandes de fusion ont été fusionnées et six mises en production faites.

- BACKLOG.md : réalignement du 08/09, l'ancien part en historisation/. Relu
  sur la forge (89 tickets, 115 PR, 3 jalons) et sur le serveur. Nouveau
  critère de sortie J2 : main = develop. Les 12 tickets nés les 7 et 8 sont
  rangés. Capacité restante recalculée.
- PRD.md 1.2 : huit exigences fonctionnelles changent de statut, toutes vers
  le haut ; état de déploiement vérifié à 18 h 57 ; réserves écrites (#205,
  #208, #211-#215) ; risques du jour.
- journal/j6.md, j7.md : lundi et mardi reconstruits depuis Git et l'API de
  la forge, heure de Paris, au format de j5.md.
- journal.md : entrées J6 et J7, les blocs oraux marqués « à compléter au
  point ».

Le rapport EC02 lui-même reste à écrire : le ticket ne se ferme pas ici.
front+api : une heure sans mesure creuse un trou au lieu de passer pour une droite (#211)
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 21s
Intégration / Tableau de bord — dépendances, tests et construction (pull_request) Successful in 45s
Intégration / Python — qualité, tests et dépendances (pull_request) Successful in 6m11s
ed75470bf4
Cinq sites sur sept portaient ce matin une heure sans moyenne. Leur courbe
passait de 07:00 à 09:00 par un segment droit, et 08:00 ne figurait même pas
sur l'axe : les étiquettes se construisent depuis les points reçus, donc l'axe
se resserrait en silence. Rien à l'écran ne disait qu'il manquait une heure.

Deux causes, une de chaque côté.

Côté base, `SQL_SERIE_HEURE` et `SQL_SERIE_JOUR` portaient
`and h.moyenne_kw is not null`. Le seau existe dans `mesure_horaire`, sa
moyenne est nulle parce que toutes ses minutes ont été écartées, et la requête
le supprimait de la réponse au lieu de le rendre. Le filtre part ; `sum()` rend
`null` quand tous les sites du seau sont muets, et le point traverse.

Le contrat déclare donc `valeur_kw: float | None`. Trois endroits s'en
trouvaient exposés et sont protégés : `round(float(None))` levait, la pointe
et le creux comparaient `None` à un flottant, et l'écart à la référence
divisait sur un trou.

Côté écran, un `null` ne devient jamais un zéro — un zéro dirait « le parc a
consommé zéro », ce qui est une mesure, alors que l'absence de mesure n'en est
pas une. Chart.js ne dessine pas de barre, l'espace reste, l'heure garde son
étiquette. L'infobulle nomme le trou, et un clic dessus n'ouvre plus un panneau
de traçabilité vide.

Deux défauts trouvés en route, hors énoncé du ticket mais dans son sujet :

- `moyenneDe` sommait les `null` comme des zéros tout en les gardant au
  dénominateur. La ligne de moyenne du graphique affichait donc une
  consommation inférieure à la réalité, sans que rien ne le dise.
- `MicroCourbe` est la seule VRAIE courbe de l'écran, et elle filtrait les
  trous avant de placer ses abscisses. Le trait les traversait et l'axe se
  resserrait — exactement le défaut du titre du ticket, en vignette. Elle
  trace maintenant un tronçon par suite continue, pose une pastille sur les
  mesures isolées, et son équivalent textuel annonce les heures manquantes.

Le critère `spanGaps: false` de l'énoncé n'est pas repris : il vise une courbe
Chart.js, or les deux graphiques nommés sont des diagrammes en BARRES, où une
valeur nulle laisse déjà un vide. L'intention est tenue autrement.

Éprouvé : quatre cas côté API et huit côté front, tous vérifiés en échec sur
le code d'avant. 235 cas API, 481 cas front, mypy --strict et ruff au vert.

Hors périmètre, comme le dit le ticket : combler les heures manquantes.
Celui-ci les MONTRE.
front : la synthèse du parc annonce l'état des valeurs qu'elle additionne (#213)
All checks were successful
Intégration / Tableau de bord — dépendances, tests et construction (pull_request) Successful in 44s
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 17s
Intégration / Python — qualité, tests et dépendances (pull_request) Successful in 5m52s
5c49e8033a
La rangée de tête portait quatre pavés, aucun ne disait ce que valaient les
valeurs additionnées à cet instant. « Disponibilité moyenne » est un indicateur
de la JOURNÉE, calculé par la zone argent sur la grille des 1 440 minutes : on
pouvait lire « 98,5 % » pendant que deux sites sur sept portaient une valeur
suspecte et un troisième rien du tout, ce qui était l'état de la production ce
matin. Les deux chiffres répondent à deux questions ; sans les mots pour les
séparer, le premier répondait à tort à la seconde.

Un cinquième pavé, « Qualité à l'instant », compte les sept valeurs : combien
mesurées, reconstituées, suspectes, absentes. Il prend une teinte de tension
dès qu'une au moins n'est pas bonne, et sur un parc sain il le DIT plutôt que
de rester vide — un pavé vide se lit comme un écran qui n'a pas fini de
charger.

Le compte dérive de `store.sites`, déjà chargé pour le tableau juste en
dessous : aucun second appel, et surtout aucune seconde source. Les deux
lectures doivent dire la même chose, sinon l'une des deux ment.

Trois choix à contredire s'ils ne conviennent pas :

- Un site jamais mesuré (`qualite` nulle) et un site mesuré sans valeur
  (`critical`) tombent dans le même seau « absente ». La nuance existe et se
  lit dans le tableau ; un compte de tête qui la porterait ferait cinq
  nombres là où trois suffisent.
- Les états à zéro ne s'affichent pas. « 0 suspecte » sur un parc sain ferait
  trois lignes de zéros, et l'œil cesserait de les lire le jour où l'un ne
  serait plus à zéro.
- La teinte porte sur le libellé, pas sur la valeur : « 4/7 mesurées » n'est
  pas un chiffre alarmant en soi, c'est l'indicateur qui demande un regard.
  Elle redouble le compte écrit en dessous et ne le remplace jamais (RGAA
  1.4.1), comme le fait déjà « sous la cible » à côté.

Éprouvé : six cas, dont cinq vérifiés en échec sur le composant d'avant.
471 cas front au vert, construction Vite au vert.

Hors périmètre, comme le dit le ticket : la disponibilité du jour, qui vient
de `qualite_jour` et que la zone argent calcule. On ajoute une lecture, on
n'en corrige aucune.
front : une légende dit ce que « suspecte » et « reconstituée » veulent dire (#212)
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 43s
Intégration / Workflows — lint et audit de sécurité (pull_request) Successful in 21s
Intégration / Python — qualité, tests et dépendances (pull_request) Successful in 5m46s
f97971839a
Le #199 a mis les états de qualité à l'écran, avec leur pastille. Personne
n'expliquait ce que ces mots veulent dire ni d'où ils viennent. Un exploitant
qui lit « suspecte » sur un site ne sait pas s'il doit s'inquiéter du capteur,
de la valeur, ou des deux. Un jury ne sait pas si c'est nous qui l'avons conclu
ou la source qui l'a déclaré — et la réponse est justement intéressante,
puisque les deux peuvent diverger et que la divergence est elle-même une
information (migration 0007).

Une légende repliée, sous le tableau de la vue Parc, nomme les quatre états et
les quatre méthodes d'imputation. Son préambule pose la distinction qui porte
tout le reste : ces états sont CONCLUS par notre traitement après contrôle, ils
ne sont pas déclarés par la source.

Les déclencheurs ne sont pas paraphrasés. Ils sont relevés sur la fonction de
`services/etl/etl/silver/mesure.py` qui décide, dans son ordre de gravité :
un trou hors bornes prime sur tout, une valeur reconstituée reste plus fiable
qu'une valeur suspecte conservée telle quelle. Une explication réécrite de
mémoire divergerait du code le jour où l'un des deux bougerait, sans que rien
ne le signale.

Les textes vivent dans `api/glossaire.js`, jamais dans le composant : c'est
déjà là que les libellés se traduisent. Le composant parcourt les tables
exportées plutôt que de recopier les clés, si bien qu'un cinquième état
apparaîtrait de lui-même. Les cas d'essai LISENT ces mêmes tables : un cas qui
répéterait les textes passerait au vert le jour où le composant cesserait de
les lire, c'est-à-dire le jour où ce ticket serait défait.

Deux choix :

- Sous le tableau et non en tête. C'est là qu'on rencontre ces mots ; une
  légende posée avant expliquerait un vocabulaire que le lecteur n'a pas
  encore vu.
- Un `<details>` natif et non un dispositif à nous. Il s'ouvre au clavier,
  s'annonce comme repliable, son contenu se trouve avec la recherche du
  navigateur une fois ouvert, et il ne coûte pas une ligne de JavaScript.

Éprouvé : six cas, 471 cas front au vert, construction Vite au vert.

Hors périmètre, comme le dit le ticket : la refonte de la vue Parc (#184), et
tout nouvel état — les quatre de la migration 0007 sont les quatre.
front : le profil du parc coupe son trait sur un trou au lieu de plonger à zéro (#211)
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 22s
Intégration / Tableau de bord — dépendances, tests et construction (pull_request) Successful in 41s
Intégration / Python — qualité, tests et dépendances (pull_request) Successful in 5m36s
ff9ef62970
`ProfilHoraireParc` consomme la même série que `MicroCourbe` et `GraphiqueSite`
— `store.profilParc`, servi par `serie_parc` — mais n'avait pas été adapté aux
points nuls introduits par ce ticket. Deux conséquences, toutes deux sur le
graphique principal de l'écran parc :

- `y(null)` valant `Y1 - (null / haut) * (Y1 - Y0)`, soit la ligne de base, le
  trait PLONGEAIT À ZÉRO sur chaque heure sans mesure. C'est pire que la droite
  que le ticket corrige : une panne se lisait comme une consommation nulle
  relevée.
- `null < 1000` étant vrai, le trou devenait le minimum du parc, et le résumé
  lu à voix haute annonçait « minimum — kW à 14 h ».

Le trait se trace donc par tronçons continus, une polyligne par tronçon, avec
une pastille pour les heures mesurées seules entre deux trous — une polyligne
d'un seul sommet ne dessine rien. L'abscisse reste celle du rang dans la série,
trous compris : l'axe des heures ne se resserre pas. Les extrêmes se cherchent
sur les seules heures mesurées, et le résumé annonce le nombre de trous, qu'un
lecteur d'écran ne peut pas voir.

Une fenêtre entièrement creuse se dit désormais « aucune mesure sur les 24
heures de la période » plutôt que « indisponible » : les seaux sont là, aucun
n'a de valeur, et les deux ne sont pas la même chose.
Merge pull request 'front : la synthèse du parc annonce l'état des valeurs qu'elle additionne' (#218) from lenaic/213-qualite-instantanee into develop
Some checks failed
Intégration / Contrôles statiques du dépôt (push) Waiting to run
Intégration / Workflows — lint et audit de sécurité (push) Waiting to run
Intégration / Tableau de bord — dépendances, tests et construction (push) Has been cancelled
Intégration / Python — qualité, tests et dépendances (push) Has been cancelled
513364b499
Merge pull request 'front : une légende dit ce que « suspecte » et « reconstituée » veulent dire' (#219) from lenaic/212-legende-qualite into develop
Some checks failed
Intégration / Tableau de bord — dépendances, tests et construction (push) Successful in 43s
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 16s
Intégration / Python — qualité, tests et dépendances (push) Has been cancelled
9f8dc30781
Merge pull request 'front+api : une heure sans mesure creuse un trou au lieu de passer pour une droite' (#217) from lenaic/211-trous-visibles into develop
All checks were successful
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 21s
Intégration / Tableau de bord — dépendances, tests et construction (push) Successful in 43s
Intégration / Python — qualité, tests et dépendances (push) Successful in 5m30s
b51af033af
docs : le suivi intègre les trois tickets front fusionnés dans la soirée (#48)
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 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 5m35s
d9d537b0bf
Cette branche a été écrite à 18 h 57 et arrêtait ses chiffres à cette heure.
Entre-temps, #211, #212 et #213 ont été fusionnés — PR #217 à 21 h 20, #218 et
#219 à 21 h 17. Onze passages du PRD et du backlog les décrivaient encore comme
des réserves ouvertes, dont deux tableaux d'arbitrage et l'ordre de sacrifice.
Fusionner en l'état aurait mis dans `main` un état des lieux faux de trois
heures, sur les deux fichiers que le jury lit en premier.

Ce qui change, factuellement :

- PRD, EF-04 : la réserve sur la courbe est levée, pas « ouverte ».
- PRD, EF-11 : #212 et #213 sortent des réserves, #215 y reste seul.
- PRD et backlog, ordre de sacrifice : #215 devient le seul candidat.
- Backlog, rang 1 : la mise en production porte 10 PR et non 7.
- Backlog, rangs 8 et 12 : barrés, avec la date et le numéro de PR.
- Backlog, « ouvert et pas commencé » : seul #215 y reste.
- Backlog, répartition : #211 ne revient plus à Marvin.
- Backlog, capacité : la charge passe de 6,5 à 5,5 jours-personne, une
  demi-journée et deux quarts retirés, avec l'heure d'arrêt des chiffres.

La règle « le Tech Lead ne prend pas de front » est corrigée plutôt que
supprimée : elle a sauté ce soir, les trois tickets étaient en EC05 et personne
ne les avait pris. Le noter vaut mieux que laisser une règle que le fichier
lui-même contredit trois paragraphes plus bas.

`tests/ci/test-liens-markdown.sh` : 263 liens dans 81 fichiers, tous valides.
Merge pull request '[48] Suivi de projet : J6, J7, PRD 1.2 et backlog réalignés sur la forge et le serveur' (#216) from gabriel/48-suivi-j6-j7 into develop
All checks were successful
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 21s
Intégration / Tableau de bord — dépendances, tests et construction (push) Successful in 42s
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 7s
Intégration / Workflows — lint et audit de sécurité (pull_request) Successful in 19s
Infra Ansible / Playbooks Ansible valides (pull_request) Successful in 1m2s
Intégration / Python — qualité, tests et dépendances (push) Successful in 5m54s
Intégration / Python — qualité, tests et dépendances (pull_request) Successful in 5m41s
1c3bda6ce0
Reviewed-on: https://10.105.200.41/g2/enervision/pulls/216
Reviewed-by: lenaic <lenaic@noreply.10.105.200.41>
lenaic requested review from gabriel 2026-09-08 21:36:36 +00:00
gabriel approved these changes 2026-09-08 21:37:18 +00:00
lenaic merged commit 03bfb47e59 into main 2026-09-08 21:44:08 +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!220
No description provided.