Co-Study4Grid / docs /architecture /development-cycle.md
github-actions[bot]
Deploy 7688ef1
13d4e44
|
Raw
History Blame Contribute Delete
42.3 kB
# Cycle de développement de Co-Study4Grid
> Résumé du cycle de développement reconstruit à partir du `git log`
> (358 commits, du 28/03/2026 au 30/04/2026), du `CHANGELOG.md` et
> des descriptions de PRs depuis GitHub. Pour les 65 PRs antérieures
> au rebrand (`ExpertAssist`, 14/02 → 28/03/2026), absentes du clone
> local de `Co-Study4Grid`, les métriques s'appuient sur le clone
> de `marota/ExpertAssist` (783 commits, premier commit `9218c4c`
> du 13/02/2026) en plus des descriptions de PR.
Ce document est une vue d'ensemble chronologique des grandes phases
du projet. Pour les détails par sujet, voir les docs ciblées :
- Audit qualité 2026-04 : [`code-quality-analysis.md`](code-quality-analysis.md)
- Refactor `App.tsx` : [`app-refactoring-plan.md`](app-refactoring-plan.md),
[`phase2-state-management-optimization.md`](phase2-state-management-optimization.md)
- Pipeline PyPSA-EUR → XIIDM : [`../data/pypsa-eur-osm-to-xiidm.md`](../data/pypsa-eur-osm-to-xiidm.md)
- Audit de parité standalone : [`../../frontend/PARITY_AUDIT.md`](../../frontend/PARITY_AUDIT.md)
## Vue d'ensemble
### Chronologie
```mermaid
gantt
title Cycle de développement de Co-Study4Grid
dateFormat YYYY-MM-DD
axisFormat %d/%m
section 1. ExpertAssist
Bootstrap (pré-PR #1) :done, p11, 2026-02-01, 2026-02-13
Bouclage métier (#1–#15) :done, p12, 2026-02-14, 2026-02-22
Maturation UX (#16–#24) :done, p13, 2026-02-22, 2026-03-01
Robustesse + CI (#25–#46) :done, p14, 2026-03-01, 2026-03-10
Persistance + actions (#47–#56) :done, p15, 2026-03-10, 2026-03-22
Two-step + rebrand (#57–#65) :done, p16, 2026-03-22, 2026-03-28
section 2. 0.5.0
Catalogue + perf + tabs (#66–#90) :done, p2, 2026-03-29, 2026-04-14
section 3. Consolidation
0.6.0 (refactor + standalone auto) :done, p3a, 2026-04-14, 2026-04-20
0.6.5 (qualité + parité) :done, p3b, 2026-04-20, 2026-04-22
section 4. PyPSA-EUR
Pipeline + datasets (#112) :done, p4, 2026-04-16, 2026-04-24
Post-merge (overflow viewer) :done, p4b, 2026-04-24, 2026-04-30
section 5. UX iterations
Design tokens + ActionCard :done, p5a, 2026-04-26, 2026-04-29
Tier warning system + popover fix :done, p5b, 2026-04-29, 2026-05-03
```
### Architecture d'ensemble
```mermaid
graph LR
User(["Opérateur réseau"])
subgraph Frontend
ReactSPA["React 19 + TS<br/>(Vite dev)"]
StandaloneHTML["standalone.html<br/>(vite-plugin-singlefile)"]
end
subgraph Backend
FastAPI["FastAPI + Uvicorn<br/>:8000"]
Recommender["expert_op4grid_<br/>recommender"]
Pypowsybl["pypowsybl<br/>(load flow + NAD/SLD)"]
end
subgraph Data
Network[".xiidm + actions.json<br/>+ grid_layout.json"]
Sessions["Overflow_Graph/<br/>+ session.json"]
end
User -->|HTTP| ReactSPA
User -->|HTTP| StandaloneHTML
ReactSPA -->|axios| FastAPI
StandaloneHTML -->|axios| FastAPI
FastAPI --> Recommender
FastAPI --> Pypowsybl
Pypowsybl --> Network
Recommender --> Network
FastAPI --> Sessions
```
---
## 1. Mise en place d'une base minimale de bout en bout — ère « ExpertAssist » (14/02/2026 → 28/03/2026, PRs #1 → #65)
Le clone local n'expose plus que les commits postérieurs à la bannière
MPL-2.0 (`426b6f7`, 28/03/2026), juste après le rebrand. Mais sur
GitHub, l'historique pré-0.5.0 est riche : **65 PRs en ~6 semaines**,
sous le nom *ExpertAssist*. Ce paragraphe les consolide à partir des
descriptions de PR (et non plus du seul `CHANGELOG`, qui les agrège
sous *Earlier Development (pre-0.5.0)* sans les énumérer). Le projet
arrive en 0.5.0 (14/04/2026) avec la quasi-totalité de l'architecture
encore en place aujourd'hui ; ce qui suit est donc plus qu'un
préalable — c'est l'essentiel du socle.
### 1.1. Bootstrap — commit d'init `9218c4c` (vendredi 13/02/2026 13:25)
Le tout premier commit du repo `marota/ExpertAssist` s'intitule
sobrement *« initialisation of ExpertAssist interface »* : **44
fichiers, 2 513 insertions** poussées d'un seul jet. Le scaffolding
n'arrive donc pas vide à PR #1 — il arrive déjà fonctionnel de bout
en bout, sur ~2,5 k LoC.
**Backend (130 + 384 + 52 LoC)**`expert_backend/main.py` expose
**7 endpoints** :
```
POST /api/config GET /api/network-diagram
GET /api/branches POST /api/contingency-diagram
GET /api/voltage-levels POST /api/run-analysis (NDJSON streaming)
GET /api/pick-path ↳ via tkinter en subprocess
```
Le `RecommenderService` charge déjà le réseau via **pypowsybl**,
génère NAD pour N et N-1, et orchestre le recommender en
`StreamingResponse`. **CORS wide-open**, mount static
`/results/pdf → Overflow_Graph/`, et la fenêtre OS-native pour
choisir un fichier ou un dossier sont *présents au commit zéro*.
**Frontend (35 LoC `App.tsx` + 19 LoC `api.ts`)** — un shell React
de **3 composants** (`ConfigurationPanel` 92 LoC, `VisualizationPanel`
33 LoC, `ActionFeed` 43 LoC). Le client `axios` ne propose que
`updateConfig` / `getBranches` / `runAnalysis`. Le titre de l'app
est encore **« Expert_op4grid Recommender Interface »** ;
*ExpertAssist* en sera le nom de produit.
**Standalone** — `standalone_interface.html` est déjà à **755 LoC**.
Il sera maintenu en parallèle de la SPA pendant toute la durée de
l'ère ExpertAssist.
**Outillage de dev embarqué** — 5 scripts ad-hoc pour reproduire les
problèmes observés pendant le bootstrap : `test_api_stream.py`,
`test_n1_api.py`, `test_voltage_api.py`, `verify_n1_simulation.py`,
`inspect_metadata.py`, plus `repro_stuck.py` et `fix_zoom.py`.
Deux Overflow Graphs PDF sont commitées comme exemple-jouet.
> Le projet *naît* avec le streaming NDJSON et le triangle
> *config → branche → analyse* déjà bouclé. Tout ce qui suit est
> de l'enrichissement.
### 1.2. Bouclage métier de bout en bout (14 → 22/02/2026, PRs #1 → #15)
Première semaine de PRs : on rend le triangle *réseau → contingence →
action* utilisable de bout en bout. **À l'arrivée (commit `35646f6`,
21/02)** :
| Fichier | Init | Après PR #15 | × |
|-----------------------------------|----------|--------------|---|
| `frontend/src/App.tsx` | 35 | 617 | ×17 |
| `frontend/src/components/VisualizationPanel.tsx` | 33 | 260 | ×8 |
| `expert_backend/main.py` | 130 | 233 | ×1,8 |
| `expert_backend/services/recommender_service.py` | 384 | 636 | ×1,7 |
| `standalone_interface.html` | 755 | 2 100 | ×2,8 |
| Endpoints backend | 7 | 13 | +6 |
Endpoints ajoutés sur la fenêtre : `/api/nominal-voltages`,
`/api/action-variant-diagram`, `/api/element-voltage-levels`,
`/api/focused-diagram`, `/api/actions`, `/api/simulate-manual-action`.
Les PRs, par ordre de merge :
- **PR #1 (14/02)***NAD rendering for large grids* : `boostSvgForLargeGrid()`
(scale fonts/labels/legends en `sqrt(taille/référence)` au-delà de
1,5× la référence), extraction des helpers backend (`_load_network`,
`_load_layout`, `_default_nad_parameters`, `_generate_diagram`),
endpoints `GET /api/element-voltage-levels` + `POST /api/focused-diagram`
pour les sous-diagrammes centrés sur un équipement.
- **PR #2 (14/02)** — première version du `CLAUDE.md` (160 lignes,
référence projet pour humains et assistants).
- **PR #4 (15/02)** — README projet.
- **PR #5 (15/02)** — pan/zoom + métadata lookups optimisés.
- **PR #6 (16/02)***ActionFeed* avec données `rho` structurées.
- **PR #7 (16/02)**`/api/action-variant-diagram` + sélection
interactive d'actions sur la N-1.
- **PR #8 (16/02)** — pré-calcul de l'état réseau pour accélérer
les variantes d'action.
- **PR #9 (17/02)***highlights* des lignes en surcharge et des
cibles d'action sur la SVG.
- **PR #11 (18/02)****actions manuelles** simulables côté UI.
- **PR #12 (19/02)** — mode **delta flows** (Δ versus N-state).
- **PR #13 (19/02)** — filtres par voltage level.
- **PR #14 (20/02)** — intégration des **scores estimés** pour les
actions manuelles (la colonne *score* devient utilisable avant
simulation complète).
- **PR #15 (21/02)** — refonte majeure de la visualisation : le hook
`usePanZoom` (manipulation directe du viewBox SVG en bypass de
React), le module `svgUtils.ts` (`processSvg`, `buildMetadataIndex`
O(1), `applyOverloadedHighlights`, `applyActionTargetHighlights`,
`applyDeltaVisuals`, `boostSvgForLargeGrid`), le **multi-tab indépendant**
N / N-1 / action / overflow et le streaming SSE des résultats.
`VisualizationPanel` passe de 397 à 259 lignes.
### 1.3. Maturation UX/UI (22/02 → 01/03/2026, PRs #16 → #24)
- **PRs #16–#18 (22/02)** — assets cliquables sur les action cards,
tooltips en `position: fixed` (debord clean), overlay SLD draggable.
- **PR #19 (27/02)****Recommender Settings panel** complet :
sliders pour `min_line_reconnections`, `min_close_coupling`,
`min_open_coupling`, `min_line_disconnections`, `n_prioritized_actions` ;
flow *Apply / Close* avec backup pour annuler ; auto-apply +
banner dynamique pour le `lines_monitoring_path` ;
panneau settings tabbé *Recommender / Configurations*. La PR fixe
aussi le **bug du score 0,00 sur les disconnections** quand
`lines_monitoring_file` est actif (correction de
`expert_op4grid_recommender/action_evaluation/discovery.py`) et
re-mémoise `actions.json` côté backend pour rendre les hot-swaps
de settings instantanés.
- **PR #20 (28/02)****OverloadPanel** compact en haut de la sidebar
(lignes en surcharge N + N-1, clic = zoom), **`monitoringFactor`**
(`MONITORING_FACTOR_THERMAL_LIMITS`, default 0,95) propagé partout :
détection backend `_get_overloaded_lines`, mise à l'échelle des
`rho_before/rho_after/max_rho`, seuils de coloration des
action cards.
- **PR #21 (28/02)** — exclusion des surcharges pré-existantes en N-1
sauf si **aggravées** par la contingence.
- **PRs #22–#24 (01/03)** — filtres par type d'action ; persistence
des actions manuelles ; restructuration du layout ; *state-switching*
des SLD (le diagramme suit l'onglet actif).
### 1.4. Robustesse + CI + perfs (01 → 10/03/2026, PRs #25 → #46)
C'est la phase où la base devient testable et fiable :
- **PR #25 (01/03)****suite de tests** complète : backend
(`test_api_endpoints.py`, `test_network_service.py`,
`test_compute_deltas.py`, `test_sanitize.py`) avec un
`conftest.py` qui mocke `pypowsybl` / `expert_op4grid_recommender`
pour CI, frontend (`api.test.ts`, `svgUtils.test.ts`,
`VisualizationPanel.test.tsx`, `OverloadPanel.test.tsx`) sous
Vitest + RTL.
- **PR #28 (02/03)****CircleCI + GitHub Actions** en parallèle
(Python + React/Vitest/ESLint), `pyproject.toml` introduit. À ce
point : 80 tests backend + 66 frontend.
- **PR #26** — pan/zoom batché en interaction state.
- **PR #27** — clustering des voltage levels détectés + bucket
sous 25 kV.
- **PR #29** — exclusion des branches non monitorées.
- **PR #30** — bouton **« Make a first guess »** + warnings clarifiés.
- **PRs #31–#32** — calcul des deltas terminal-aware avec sélection
par VL, intégration de la puissance réactive.
- **PRs #33–#35** — fix SLD voltage diagram, modes
`IGNORE_RECONNECTIONS` / `PYPOWSYBL_FAST_MODE`, refactor du
`RecommenderService` pour la consistence des simulations.
- **PRs #36–#38** — recherche SLD synchronisée entre tabs,
**lazy-loading des actions** côté recommender.
- **PRs #39–#41** — fix sérialisation NaN (`Infinity``null`),
UI de non-convergence (tags *divergent*, warnings orange,
tests unitaires), réintroduction des actions manuelles différées.
- **PR #46 (10/03)** — latence de tab-switch + optimisation SVG.
### 1.5. Persistance + actions complexes (10 → 22/03/2026, PRs #47 → #56)
- **PR #47****confirmation dialogs** pour le changement de
contingence et le reload d'étude.
- **PR #48** — enrichissement de la topologie d'action avec `pst_tap`.
- **PR #49 (11/03)****save session** : `session.json` + copie du
PDF *overflow graph* + theme jaune cohérent + auto-fade des
notifications + `test_save_session.py`.
- **PR #50** — reset propre de l'état de contingence.
- **PR #51 (14/03)****islanding MW reporting** + highlights
multi-assets pour les actions combinées.
- **PR #52 (17/03)****reload session** + persistance des paires
combinées :
- `/api/list-sessions` + `/api/load-session` + bouton dédié dans
le banner.
- Restauration sans re-simulation (rho values + status tags
sauvegardés sont réaffichés tels quels ; la simulation ne se
déclenche qu'à la sélection d'une action).
- `restore_analysis_context()` côté backend pour conserver
`lines_we_care_about` (consistence du monitoring entre runs).
- Reconstruction de la topologie d'action depuis les données
sauvegardées pour les actions absentes du dictionnaire courant.
- Suppression des dialogs de changement de contingence pendant
le restore via `restoringSessionRef`.
- **PRs #53–#54** — détection PST robuste dans les actions combinées,
highlights de contingence orange.
- **PR #56 (21/03)** — **App.tsx Phase 1** : extraction de **5 hooks**
(`useSettings`, `useActions`, `useAnalysis`, `useDiagrams`,
`useSession`) ; `App.tsx` passe **2 100 → 969 lignes** au merge
(la cible de la PR description était ~800, atteinte dans les PRs
suivantes par cleanup). C'est aussi la PR qui introduit
`docs/app-refactoring-plan.md`.
### 1.6. Two-step analysis, replay-ready logging et rebrand (22 → 28/03/2026, PRs #57 → #65)
Dernière vague avant le tag 0.5.0, qui pose la majorité des
contrats utilisateur encore en vigueur :
- **PR #57 (22/03)****split de l'analyse en deux étapes** :
`runAnalysisStep1` (POST → `can_proceed`, détection des
surcharges) puis `runAnalysisStep2Stream` (POST avec
`selected_overloads` / `all_overloads` / `monitor_deselected`,
réponse streaming NDJSON). Sélection intelligente : préserve les
surcharges déjà sélectionnées si elles intersectent les nouvelles,
fallback all-detected sinon. Test coverage massive sur
`useAnalysis` (396 lignes).
- **PR #58 / #60***interface discrepancies* React/standalone.
- **PR #59 (24/03)****persistance config** sur disque :
`config.default.json` (templaté, tracké) + `config.json`
(auto-créé, gitignoré) + endpoints `GET/POST /api/user-config`.
Remplace l'ancien `localStorage`.
- **PR #61 (25/03)****action de délestage de charge** intégrée :
paramètre `MIN_LOAD_SHEDDING`, calcul du delta MW shedded dans
`_enrich_actions` et `simulate_manual_action`, badges VL verts
cliquables et `get_load_voltage_levels_bulk()` côté backend.
- **PR #62 (25/03)** — colonne **MW Start** sur les action scores
(line disconnection : `abs(p_or)` ; PST tap : `abs(p_or)` du PST ;
load shedding : `load_p` ; open coupling : somme des `abs(p_or)`
des virtuelles, etc.).
- **PR #63 (26/03)****SLD impact highlights** : 4 styles
distincts (orange contingencies / yellow actions / dashed-orange
overloads / purple breakers), avec lookup d'équipement à fallback
multiples (sanitization dot/underscore + substring SVG ID),
enrichissement backend `pst_tap` + `substations` + `switches`.
- **PR #64 (27/03)****interaction-logging replay-ready** :
`InteractionLogger` singleton, 50+ types d'événements, `seq` +
ISO timestamp + correlation ID pour les paires async start/complete,
sauvegarde dans `interaction_log.json`, design doc 655 lignes
+ `docs/interaction-logging.md` (456 lignes).
- **PR #65 (28/03)****rebrand `ExpertAssist` → `Co-Study4Grid`**
(1 commit, 18 fichiers, +26/−1998 — purge des références au
vieux nom et nettoyage). C'est ce point qui ouvre l'historique
git visible localement.
#### Flux d'analyse en deux étapes (PR #57)
Le contrat utilisateur principal posé par cette dernière vague :
```mermaid
sequenceDiagram
actor U as Utilisateur
participant F as Frontend
participant B as Backend
participant R as Recommender
U->>F: Sélectionne contingence
F->>B: POST /api/contingency-diagram
B-->>F: SVG N-1 + lines_overloaded
F-->>U: Affiche N-1 + halos surcharges
U->>F: Clic « Run analysis »
F->>B: POST /api/run-analysis-step1
B->>R: detect_overloads()
R-->>B: overloads + can_proceed
B-->>F: { overloads, can_proceed }
F-->>U: Liste les surcharges détectées
U->>F: Coche les surcharges à résoudre
F->>B: POST /api/run-analysis-step2 (NDJSON)
B->>R: resolve(selected_overloads)
R-->>B: PDF prêt
B-->>F: { type:"pdf", path }
R-->>B: scores d'actions
B-->>F: { type:"result", actions }
F-->>U: Action cards + Overflow PDF
```
> **Bilan de la phase ExpertAssist** : 65 PRs sur 6 semaines, des
> couches de scaffold + visualisation NAD multi-tab à un workflow
> N-1 deux-étapes streamé, avec persistance de session, interaction
> logging, mocks de CI et la première extraction d'`App.tsx` en hooks.
> La 0.5.0 (14/04/2026) ne fait qu'apposer un tag sur cet ensemble et
> ajouter le catalogue d'actions complet (PST, curtailment) +
> les vectorisations perf — l'architecture qui sous-tend ce qui
> existe aujourd'hui est bâtie ici.
## 2. Premier tag de release : 0.5.0 (29/03 → 14/04/2026, PRs #66 → #~95)
Le tag 0.5.0 (14/04/2026) entérine **l'architecture ExpertAssist
au complet** (cf. § 1) et y ajoute, en deux semaines :
1. les optimisations perf qui rendent le réseau français interactif,
2. le catalogue d'actions remédiales final (curtailment, PST tap,
load shedding au nouveau format `loads_p`/`gens_p`),
3. la première décomposition de `App.tsx` côté composants,
4. les onglets de visualisation détachables pour multi-écran.
> **Note de réconciliation** : les briques *save / reload session*,
> *interaction-logging rejouable*, *MW Start* et *highlights SLD*
> (PRs #49 / #52 / #62 / #63 / #64) sont en fait pré-rebrand —
> elles arrivent en 0.5.0 par le simple effet du tag, sans nouveau
> code dans cette fenêtre. Voir § 1.5–1.6.
### 2.1. Vectorisation backend (PR #66, 29/03)
Première PR post-rebrand. Cible la latence de
`simulate_manual_action` sur le réseau français complet (~10 k
branches) :
| Étage | Avant | Après | Speedup |
|------------------------------------|-----------|----------|------------|
| `care_mask` + détection surcharges | 12,17 s | 0,01 s | **1 100×** |
| Extraction des flux | 0,82 s | 0,06 s | **13×** |
| Calcul des deltas terminal-aware | 0,47 s | 0,01 s | **47×** |
| Cache d'observations (boucle) | — | — | **~65×** |
| **Total simulation manuelle** | **16,5 s**| **4,0 s**| **4×** |
Tests dédiés : `test_vectorized_monitoring.py`,
`test_cache_synchronization.py`, `test_performance_budgets.py`
(SLA logique sous 50 ms).
Une stratégie *viewport-based subsets* (50× de payload) a été
prototypée puis **rejetée** dans la même PR — perte d'intégrité du
highlighting global ; les retours d'expérience vivent dans
`docs/proposals/rendering-lod-strategies.md`.
### 2.2. Polish UI + parité standalone (PRs #69 → #71)
- **PR #69** — restauration du chargement de `grid_layout.json`
(fix régression layout) + tests de non-régression.
- **PR #70** — fix latence affichage *Overflow Graph* + tab-switch
standalone, throttling `datalist`, garde-fou zoom sur match exact,
fix `NaN` dans le SVG boost.
- **PR #71** — uniformisation des highlights React + standalone :
suppression des `drop-shadow`, opacité solide pour overloads,
actions et contingencies, fix dimming des cibles d'action.
### 2.3. Catalogue d'actions remédiales final (PRs #72, #73, #78)
- **PR #72 (01/04)** — branche `feature/renewable-curtailment-integration` :
`simulate_manual_action` retourne `action_topology` immédiatement
(sync UI instantanée), descriptions unifiées (`'GEN' at voltage
level 'VL'`), enregistrement des actions manuelles dans le
registre central (SLD highlights), fix du signe MW
curtailé/sheddé, badges interactifs (générateurs/charges/VL).
- **PR #73 (07/04)** — support du **nouveau format `loads_p` / `gens_p`**
pour load shedding et curtailment (active power setpoints au lieu
de `bus = -1`) :
- Backward-compat plein avec le format legacy
(`loads_bus` / `gens_bus` à `-1`).
- `_compute_load_shedding_details`, `_compute_curtailment_details`,
`_mw_start_*` détectent les deux formats.
- 553 lignes de tests dédiées (`test_power_reduction_format.py`).
- **MW configurable** côté UI + ré-simulation depuis la table de
score.
- **PR #78 (09–11/04)** — **PST tap re-simulation** :
`_compute_pst_details()` extrait `low_tap` / `high_tap` du
network manager ; `simulate_manual_action(target_tap=…)` clamp
automatiquement aux bornes ; UI éditable (purple theme) avec
bouton *Re-simulate* per-action.
### 2.4. Décomposition `App.tsx` post-rebrand (PRs #74, #75)
- **PR #74 (08/04, Phase 1 *Component Decomposition*)**
extraction des 4 composants top-level hors d'`App.tsx` :
`Header`, `SettingsModal` (qui reçoit le `SettingsState` complet
du hook `useSettings`), `ReloadSessionModal`, `ConfirmationDialog`.
`App.tsx` passe de **~1 000 → ~650 lignes**.
- **PR #75 (08/04, Phase 2 *State Management Optimization*)**
**rationalisation des re-renders** :
- 9 wrappers cross-hook mémoizés via `useCallback`.
- `clearContingencyState()` / `resetAllState()` extraits pour
déduplication (25+ lignes de resets manuels par appelant).
- 8 callbacks JSX inline → handlers nommés `useCallback`.
- 9 settings groupés dans un `RecommenderDisplayConfig`
mémoizé (un prop au lieu de 9 vers `ActionFeed`).
- **`React.memo`** sur les composants lourds : `VisualizationPanel`
(1 266 LoC), `ActionFeed` (~500), `OverloadPanel`.
```mermaid
graph LR
A["App.tsx<br/>~2 100 LoC<br/>(pic, ~PR #55)"] -- "PR #56 (21/03)<br/>5 hooks extraits" --> B["~969 LoC<br/>au merge"]
B -- "PRs cleanup<br/>22-28/03" --> B2["~800 LoC"]
B2 -- "PR #74 (08/04)<br/>Header + 3 modals" --> C["~650 LoC"]
C -- "PR #75 (08/04)<br/>useCallback + React.memo" --> D["~650 LoC<br/>memoizé"]
D -- "PR #109 (post-0.5.0)<br/>useContingencyFetch + Sidebar" --> E["~1 150 LoC<br/>orchestration hub"]
```
### 2.5. Onglets de visualisation détachables (PRs #84, #87, #90)
Refonte multi-écran complète :
- **PR #84 (13/04)** — chacun des 4 onglets (N / N-1 / action /
overflow) peut être **détaché dans une fenêtre séparée** :
- Hook `useDetachedTabs` qui pilote le cycle de vie des popups
(cleanup `pagehide` + polling pour les fermetures via chrome
navigateur).
- `TabPortal` (React portals) qui *relocate* le sous-arbre sans
le démonter — préserve `MemoizedSvgContainer`, refs, viewBox
auto-zoomé.
- `usePanZoom` et `SldOverlay` bindent désormais leurs listeners
`mousemove`/`mouseup` sur le `ownerWindow` de l'élément, pour
que drag-pan et drag-move continuent de fonctionner dans les
popups.
- 2 nouveaux types d'événement : `tab_detached`, `tab_reattached`
(replay-ready).
- **PR #86 (14/04)** — warmup du test de budget perf sur petit
réseau pour absorber le cold-start CI (mocks, BLAS/pandas froid).
- **PR #87 (14/04)** — `InspectSearchField` custom (DOM dans le
même sous-arbre que l'input) qui remplace le couple
`<input list>` + `<datalist>`. Contourne un bug Chromium où le
datalist natif s'ouvre dans la mauvaise fenêtre quand le champ
est dans un popup.
- **PR #90 (14/04)** — fix : sélection d'une action dans le popup
ne *bascule plus* la fenêtre principale sur l'onglet `action`
(le placeholder "Detached" ne réapparaît plus à chaque clic).
### 2.6. Tableau récapitulatif des features 0.5.0 « réellement nouvelles »
| Feature | PR | Date |
|----------------------------------------|-------------------|----------|
| Vectorisation backend (4× speedup) | #66 | 29/03 |
| Polish highlights React + standalone | #69, #70, #71 | 30/03 |
| Curtailment integration + manual SLD | #72 | 01/04 |
| Format `loads_p`/`gens_p` + MW édit. | #73 | 07/04 |
| `App.tsx` Phase 1 (composants) | #74 | 08/04 |
| `App.tsx` Phase 2 (mémoization) | #75 | 08/04 |
| PST tap re-simulation | #78 | 09–11/04 |
| Onglets détachables | #84, #86, #87, #90| 13–14/04 |
> Les features *save / reload / interaction-logging / MW Start /
> SLD highlights / two-step analysis* étaient déjà en place avant
> le tag 0.5.0 — voir § 1.5 et § 1.6.
## 3. Consolidation de la base de code et de sa qualité (0.6.0 → 0.6.5, 14/04 → 22/04/2026)
Les features ralentissent au profit de l'architecture, de la dette
technique et de la non-régression :
- **Phase 2 hooks `App.tsx` (PR #109)** — extraction de `useContingencyFetch`,
`useDiagramHighlights`, `AppSidebar`, `SidebarSummary`,
`StatusToasts`. Le hub remonte de ~650 à **~1 150 lignes** (le
prix à payer pour réinjecter de l'orchestration cross-hook propre).
Voir le diagramme d'évolution en § 2.4.
- **Décomposition backend (PR #104/#106)** : injection de
dépendances pour préserver les `@patch` existants côté tests.
```mermaid
graph TB
subgraph "Avant"
SMA1["simulate_manual_action<br/>599 LoC"]
CS1["compute_superposition<br/>285 LoC"]
AM1["analysis_mixin<br/>1 116 LoC"]
DM1["diagram_mixin<br/>974 LoC"]
end
subgraph "Après PR #104 / #106"
SMA2["simulate_manual_action<br/>146 LoC"]
CS2["compute_superposition<br/>108 LoC"]
AM2["analysis_mixin<br/>509 LoC"]
DM2["diagram_mixin<br/>469 LoC"]
SH["simulation_helpers.py"]
AD["services/analysis/<br/>4 modules"]
DD["services/diagram/<br/>7 modules"]
end
SMA1 --> SMA2
SMA1 --> SH
CS1 --> CS2
CS1 --> SH
AM1 --> AM2
AM1 --> AD
DM1 --> DM2
DM1 --> DD
```
- **Décomposition frontend** : `svgUtils.ts` 1 807 → 60 lignes
+ 8 modules focalisés (`idMap`, `metadataIndex`, `svgBoost`,
`fitRect`, `deltaVisuals`, `actionPinData`, `actionPinRender`,
`highlights`).
- **Standalone auto-généré** (PR #101) : `vite-plugin-singlefile`
produit `dist-standalone/standalone.html` ; le
`standalone_interface.html` manuel est gelé puis renommé `_legacy`.
Source unique = `frontend/src/`.
- **Garde-fou parité 4 couches** (statique, fidélité de session,
séquence de gestes, **Layer 4 user-observable invariants**)
+ Playwright E2E.
- **Continuous code-quality** (PR #104) : `code_quality_report.py`
+ `check_code_quality.py` + workflow GitHub Actions/CircleCI
publiant le rapport au job summary, plafonds LoC, zéro `print()`
/ `any` / `@ts-ignore`.
- **Perf SVG DOM recycling** (PR #108) : `/api/contingency-diagram-patch`
& `/api/action-variant-diagram-patch`, ~80 % plus rapide sur les
bascules d'onglets, payload 27 → 5,5 MB.
- **Réorganisation `docs/`** en `features/ performance/{,history/}
architecture/ proposals/ data/` avec index par dossier ;
trois propositions LoD overlapping fusionnées.
- **Ruff E9/F**, `CONTRIBUTING.md`, `.editorconfig`, `.env.example`,
`CORS_ALLOWED_ORIGINS` configurable, ~234 nouveaux tests unitaires
sur les helpers extraits.
## 4. Création d'un jeu de données open-source européen — PyPSA-EUR (16/04 → 24/04/2026, PR #112)
Branche `feat/pypsa-eur-network-scripts`, mergée le 24/04. Construit
un pipeline reproductible PyPSA-EUR → XIIDM, livré avec deux jeux
de données engagés dans `data/` :
```mermaid
flowchart LR
A[("PyPSA-EUR<br/>europe.nc")] --> B[build_pipeline.py]
B --> C[convert_pypsa_to_xiidm.py]
OSM[("OpenStreetMap")] --> E[fetch_osm_names.py]
C --> G[add_detailed_topology.py]
E --> G
G --> H[calibrate_thermal_limits.py]
H --> I[("XIIDM calibré 75 GW")]
I --> J[generate_n1_overloads.py]
I --> K[regenerate_grid_layout.py]
J --> L[("data/pypsa_eur_fr400")]
K --> L
J --> M[("data/pypsa_eur_fr225_400")]
K --> M
L --> APP[Co-Study4Grid]
M --> APP
```
- **`41005e0`** (16/04) : pipeline initial *PyPSA-EUR France 400 kV
build & conversion*.
- **`c2a16ca`** : première version chargeable de la grille PyPSA.
- **`bd43044`** : noms OSM réels intégrés au pipeline et affichés dans l'UI.
- **`b194fb3`** : correction du système de coordonnées de `grid_layout.json`.
- **`20b56b5`** : montée en échelle à **75 GW** avec **limites thermiques
calibrées sur les flux**.
- **`4680689`** : modularisation du pipeline de conversion + paramétrisation
du fetch OSM.
- **`0baa3e2`** : consolidation du pipeline, ajout de la couverture pytest
dédiée (`test_build_pipeline.py`, `test_calibrate_thermal_limits.py`,
`test_generate_n1_overloads.py`, `test_regenerate_grid_layout.py`)
et commit du jeu **`fr225_400`**.
- Scripts livrés sous `scripts/pypsa_eur/` : `build_pipeline.py`,
`convert_pypsa_to_xiidm.py`, `calibrate_thermal_limits.py`,
`add_detailed_topology.py`, `generate_n1_overloads.py`,
`fetch_osm_names.py`, `regenerate_grid_layout.py` + tests
d'intégration et de calibration N-1.
- Datasets disponibles : **`data/pypsa_eur_fr400`** (réseau 400 kV)
et **`data/pypsa_eur_fr225_400`** (mixte 225/400 kV) — premier
réseau open-source utilisable nativement par l'application en plus
du `bare_env_small_grid_test` historique.
En post-merge, sur cette nouvelle base de données, viennent les
itérations *Overflow HTML viewer* (PR #116, toggle Hierarchical/Geo,
mise à l'échelle des canvas géographiques), un fix d'islanding
(`d9b1652`, 29/04) et le rafraîchissement des `CLAUDE.md` /
`README.md` en 0.6.5.
## 5. Itérations UX post-0.6.5 (26/04 → 03/05/2026, PRs #118 → #122 + suivis)
Une fois la base PyPSA-EUR stabilisée, le travail bascule sur la
critique UX consolidée dans
[`docs/proposals/ui-design-critique.md`](../proposals/ui-design-critique.md).
Quatre PRs successives — plus le suivi de surface qui les
accompagne — refondent l'apparence et la lisibilité de l'app sans
toucher au moteur d'analyse.
### 5.1. Toggle des noms de voltage levels (PR #118, 24-26/04)
- Bouton **🏷 VL** ajouté à côté du champ Inspect (`72d65ad`,
`5871b59`) : flippe la classe `nad-hide-vl-labels` sur chaque
`MemoizedSvgContainer` pour cacher / réafficher les étiquettes VL
sur les diagrammes NAD.
- La règle CSS associée (`App.css`) cible toutes les formes
pypowsybl (`foreignObject.nad-text-nodes`, `<text>` sous
`.nad-vl-nodes` / `.nad-label-nodes`, et `.nad-label-box` au
niveau racine) avec `!important` pour battre l'inline style block
injecté par pypowsybl après App.css.
- Quand les labels sont cachés, le nom du VL reste accessible au
survol via une infobulle flottante injectée par
`utils/svg/vlInteractions.ts:attachVlInteractions` (qui ajoute aussi
le clic = sélection/Inspect et le double-clic = ouverture du SLD).
- Nouvel évènement `vl_names_toggled { show }` dans le journal
d'interactions (`4ed9bdb`).
### 5.2. Migration vers les design tokens (PR #120, 26/04)
Trois phases livrées en cascade :
- **Phase A** (`47a2700`) — introduction de `src/styles/tokens.ts`
(palette de couleurs, espaces, rayons, échelle typographique) et
`src/styles/tokens.css` (variables CSS associées). Les composants
consomment `colors.brand`, `space[2]`, `radius.lg`, `text.xs`…
au lieu de littéraux.
- **Phase B** (`0e60059`) — migration des composants un par un
vers les tokens. Côté SVG, les attributs de présentation ne
passent pas par les variables CSS (Chrome ne les résout pas
toujours dans les attributs SVG) : `pinColors`, `pinColorsDimmed`,
`pinColorsHighlighted`, `pinChrome` exposent les hex pour les
appels `setAttribute('fill', …)`.
- **Phase C** (`b8de481`) — fin de la migration ; le code-quality
gate refuse désormais tout littéral hex hors de `tokens.ts`.
### 5.3. Refonte progressive-disclosure des ActionCard (PR #121, 28/04)
- `fbfb2b0` — refonte des cartes d'action en *progressive disclosure* :
une vue compacte (titre + score résumé) pour la liste, expansion
*on demand* avec les détails de simulation, le delta MW et les
badges d'équipement.
- Même PR : **plafond du halo NAD** quand on zoome très près. Le
halo des cibles d'action ne doit pas envahir tout l'écran à fort
niveau de zoom — un cap calculé sur la taille du viewBox borne le
rayon visuel.
### 5.4. Tier warning system, NoticesPanel et legend de diagramme (PR #122, 29/04 → 03/05)
C'est la PR qui materialise la *recommandation #4* de la critique UX
(« remplacer la pile de bannières jaunes par un point d'entrée
unique »).
- `9a354f2` — **`NoticesPanel.tsx`** : composant pill ⚠️
*Notices N* qui s'ancre dans le header de la sidebar et déroule
une liste consolidée des avertissements de fond (couverture
monitoring, seuils recommender, action dictionary). Chaque notice
porte sa propre `severity`, `onDismiss` et bouton d'action
optionnel. Le pill se cache de lui-même à zéro notice. Une
**legend de diagramme** unifiée est ajoutée au bas-droit du panneau
de visualisation.
- `4458d48` — **landing du pill sur la ligne contingence / N-1** :
`SidebarSummary` passe en `display:flex` + `justifyContent:
space-between` pour héberger le pill dans la même bande
horizontale que les liens contingence et surcharges N-1.
- `37f3e98` — `uxConsistency.test.tsx` : suite de garde-fous qui
vérifie que les recommandations de la critique UX restent
appliquées (palette unique, un seul header, action panel
consolidé, etc.).
### 5.5. Suivi : NoticesPanel popover en portail (`claude/fix-notice-panel-overlap-UVYra`, 03/05)
Suivi UX rapporté pendant la session de validation : sur les écrans
où le sidebar fait 25% de la largeur, le popover des notices
(largeur 320 px ancré à gauche du pill) débordait dans la zone du
diagramme et se faisait **clipper** par le `overflow: hidden` du
`AppSidebar`, donnant l'impression que la visualisation passait
au-dessus.
Correctif :
- `NoticesPanel.tsx` rend désormais le panneau via
`ReactDOM.createPortal(…, document.body)` avec `position: fixed`
et des coordonnées calculées à partir du `getBoundingClientRect()`
du pill. Ré-aligné sur le bord droit du pill pour pousser le
panneau vers la sidebar plutôt que vers la zone diagramme. Recalcul
sur `resize` et `scroll` (capture).
- `zIndex: 1000` pour battre toute hiérarchie de stacking context
ascendante. Le clic à l'extérieur exempte le contenu du portail.
- Test ajouté (`NoticesPanel.test.tsx`) : monte le composant à
l'intérieur d'un parent `overflow:hidden` et vérifie que la liste
ouverte n'est PAS un descendant du parent — elle vit sous
`document.body`. Garantit que l'échappement portail ne régressera pas.
### 5.6. Récapitulatif des PRs UX 0.6.5+
| Feature | PR | Date |
|---------------------------------------------------|---------------------|------------|
| Toggle VL-names sur les NAD | #118 | 24-26/04 |
| Design tokens (palette + espaces + typo) | #120 | 26/04 |
| ActionCard progressive disclosure + cap halo | #121 | 28/04 |
| Tier warning system + NoticesPanel + legend | #122 | 29/04-03/05|
| Fix occlusion popover Notices (portal) | `fix-notice-panel-…`| 03/05 |
## 6. Élargissement du périmètre — 0.8.0 (17/06/2026)
Après la consolidation UX de 0.6.5+ et l'intégration des modèles de
recommandation enfichables en 0.7.5 (appariée à
`expert_op4grid_recommender` 0.2.2), la 0.8.0 (appariée à 0.2.4)
n'optimise plus l'existant : elle **élargit le périmètre** sur trois
axes — le vocabulaire d'actions, la consolidation de l'UI opérateur,
et deux nouvelles façons de lancer l'outil. À partir de cette release,
la CI suit en continu la **dernière** version publiée du recommender
(plancher `>=0.2.4`) plutôt qu'une version épinglée.
### 6.1. Élargissement du vocabulaire d'actions
- **Redispatching** de bout en bout : nouveau type d'action
(monter / descendre un groupe dispatchable) calqué sur le pipeline
curtailment mais avec un delta MW **signé** éditable (±10 MW par
défaut). Plomberie complète : tag `redispatch` dans
`mw_start_scoring`, `compute_redispatch_details` /
`compute_redispatch_setpoint` côté backend, filtre + glyphe + éditeur
de carte côté frontend.
- **GST pour les paires à action d'injection** : l'estimation de paire
combinée (`POST /api/compute-superposition`, onglet *Explore Pairs*)
branche le théorème de superposition généralisé de la librairie
(`compute_combined_pair_gst`) pour couvrir les paires impliquant une
action d'injection (délestage, curtailment, redispatch) — plus
seulement les actions de topologie. `is_injection_action` côté backend
garde la classification en phase avec la librairie ; les bannières
*« ne peut être combiné pour estimation »* disparaissent de l'UI.
- **Édition interactive de topologie SLD** : un nouveau geste construit
une action remédiale en cliquant des organes de coupure sur un Single
Line Diagram (miroir de `manoeuvre_ihm` dans le recommender). Bouton
`✎ Manual action`, preview de topologie cible débouncée
(`POST /api/sld-topology-preview`, sans load flow), liste de manœuvres
interactive (`SldEditPanel`), support des actions combinées et six
nouveaux évènements de log. Contrat complet :
[`../features/sld-topology-edit.md`](../features/sld-topology-edit.md).
### 6.2. Consolidation de l'UI opérateur
- **Thème clair / sombre** : un dark mode complet, basculé depuis le
header et persisté. Le thème est un **swap de tokens**, pas une série
d'overrides par composant — le hook `useTheme` re-pointe les variables
de `styles/tokens.{css,ts}`, avec un script pré-montage pour éviter le
flash de premier rendu et une passe de lisibilité sur le chrome
NAD / SLD et le viewer d'overflow. Voir
[`../features/dark-mode.md`](../features/dark-mode.md).
- **Bande de rings de filtre d'action partagée** (`<ActionFilterRings>`)
dans la sidebar : remplace les toggles de catégorie inline, les pills
`All` / `None` et la rangée de chips de type d'action qui
s'empilaient sur l'en-tête de l'Action Overview et dans chaque modal.
Pictogrammes de sévérité colorés + pictogrammes de type d'action +
spinner *Max-loading* ; toutes les surfaces (Action Feed, Action
Overview, Manual Selection, Combine Actions, pins de l'Overflow
Analysis) consomment le même état `ActionOverviewFilters`.
- **Sidebar pliable « readability-first »** : la carte « Select
Contingency » et le feed « Remedial Actions » basculent ensemble au
moment où une contingence est validée (la sidebar ne montre que
l'affordance pertinente), un bouton `Clear` rouge sur la bande
sticky, une bulle d'info pour les surcharges, et un prop `collapsed`
qui réduit la coque à une bande de 32 px (les rings montent alors dans
la rangée d'onglets du `VisualizationPanel`).
- **Pill *Notices* à étages** : la pile (jusqu'à cinq) de bannières
jaunes superposées est remplacée par un unique pill `⚠ Notices N` dans
l'en-tête de sidebar, déroulant un panneau de notices. Le pin 📌 de
l'iframe d'overflow migre dans l'en-tête *Action pins filters*
(le bouton de toolbar autonome est retiré ; nouvel envelope
`cs4g:overflow-pins-toggled`).
### 6.3. Deux nouvelles façons de lancer l'outil
- **Game Mode** (`frontend/src/game/`) : un wrapper de session
chronométré et scoré autour du workspace d'étude, **additif et inerte
sauf si `?game=1`**. Une *session* est une liste ordonnée d'*études*
(état réseau + contingence N-1) ; le joueur doit remédier chacune avec
**au plus 3 actions** avant l'expiration d'un timer. L'intégration à
`App` se réduit à trois points de contact gardés par
`gameBridge.isGameMode()` ; les résultats exportent un
`game_session.json` qu'une compétition **Codabench** score et classe.
Contrat complet :
[`../features/game-mode-codabench.md`](../features/game-mode-codabench.md).
- **Déploiement en ligne — HuggingFace Docker Space** : une image
mono-conteneur qui sert la **SPA frontend en same-origin** avec le
backend FastAPI (port 7860), pour faire tourner l'outil comme un jeu
hébergé sans rien à installer. Build multi-stage (`VITE_API_BASE_URL=""`
+ `VITE_GAME_MODE=1`), mount `StaticFiles` **optionnel** dans `main.py`
(`COSTUDY4GRID_FRONTEND_DIST`, monté en dernier, inerte si absent — le
dev local n'est pas affecté), viewer d'overflow servi same-origin avec
`pinGlyph.js` embarqué. Les binaires (dont la grille France 225/400 kV,
désormais livrée compressée en `network.xiidm.zip`) passent par Git LFS
car l'endpoint git du Space rejette les binaires non-LFS. Setup pas à
pas sous [`../../deploy/huggingface/`](../../deploy/huggingface/).
> **Bilan de la phase 0.8.0** : le moteur d'analyse reste stable ; la
> croissance fonctionnelle est absorbée par les coutures d'extension
> déjà en place (dispatchers par type d'action, tokens de thème, bridge
> de découplage `gameBridge`) plutôt que par l'élargissement des fichiers
> chauds — `diagram_mixin.py` est même repassé sous son plafond
> (1220 → 769 lignes) par extraction de `action_patch.py` + `obs_prewarm.py`.