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 :

Vue d'ensemble

Chronologie

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

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.

Standalonestandalone_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 (Infinitynull), 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 #47confirmation 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 / #60interface 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 ExpertAssistCo-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 :

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

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.
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.
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/ :

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. 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 »).

  • 9a354f2NoticesPanel.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.
  • 4458d48landing 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.
  • 37f3e98uxConsistency.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.

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.
  • 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.
  • 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/.

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.