# 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
(Vite dev)"] StandaloneHTML["standalone.html
(vite-plugin-singlefile)"] end subgraph Backend FastAPI["FastAPI + Uvicorn
:8000"] Recommender["expert_op4grid_
recommender"] Pypowsybl["pypowsybl
(load flow + NAD/SLD)"] end subgraph Data Network[".xiidm + actions.json
+ grid_layout.json"] Sessions["Overflow_Graph/
+ 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
~2 100 LoC
(pic, ~PR #55)"] -- "PR #56 (21/03)
5 hooks extraits" --> B["~969 LoC
au merge"] B -- "PRs cleanup
22-28/03" --> B2["~800 LoC"] B2 -- "PR #74 (08/04)
Header + 3 modals" --> C["~650 LoC"] C -- "PR #75 (08/04)
useCallback + React.memo" --> D["~650 LoC
memoizé"] D -- "PR #109 (post-0.5.0)
useContingencyFetch + Sidebar" --> E["~1 150 LoC
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 `` + ``. 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
599 LoC"] CS1["compute_superposition
285 LoC"] AM1["analysis_mixin
1 116 LoC"] DM1["diagram_mixin
974 LoC"] end subgraph "Après PR #104 / #106" SMA2["simulate_manual_action
146 LoC"] CS2["compute_superposition
108 LoC"] AM2["analysis_mixin
509 LoC"] DM2["diagram_mixin
469 LoC"] SH["simulation_helpers.py"] AD["services/analysis/
4 modules"] DD["services/diagram/
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
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`, `` 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** (``) 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`.