# State Reset & Confirmation Dialogs ## Overview Co-Study4Grid manages complex analysis state across contingency selections, N-1 simulations, manual action simulations, and prioritized action results. To prevent stale data from interfering with new analyses, the application implements two complementary mechanisms: 1. **Confirmation dialogs** when switching contingency or reloading a study with existing analysis state 2. **Full state reset** when loading a new study or applying new settings --- ## Confirmation Dialogs ### When They Trigger A confirmation dialog appears when the user attempts to: - **Change contingency** (select a different branch in the datalist) while analysis results, manual simulations, or action selections exist - **Reload study** (click "Load Study") while analysis state exists ### What They Warn About The dialog informs the user that all previous analysis results will be cleared: - Analysis results and pending results - Manual simulation results - Action selections (favorited, rejected) - Action diagrams and N-1 diagrams - Overload selections - VL overlay (SLD popups) ### User Options - **Confirm**: clears all analysis state and proceeds with the action - **Cancel**: reverts to the previous state (for contingency change, the input reverts to the previous branch) ### Implementation Details #### React Frontend (`App.tsx`) The confirmation logic is implemented at the **N-1 fetch `useEffect` level**, not at the input `onChange` level. This is because: - The contingency input is a `` (datalist) that fires `onChange` on every keystroke - Intercepting keystrokes would interfere with typing partial branch names - The useEffect only fires when `selectedBranch` matches a valid branch in the `branches` array Key state: - `confirmDialog`: `{ type: 'contingency' | 'loadStudy', pendingBranch?: string } | null` - `committedBranchRef`: tracks the last confirmed branch (prevents re-prompting for the same branch) Flow for contingency change: 1. User types/selects a new branch name 2. `selectedBranch` state updates freely (allows typing) 3. N-1 useEffect fires when the value matches a valid branch 4. If `selectedBranch !== committedBranchRef.current` AND `hasAnalysisState()` is true: - Dialog is shown - Input reverts to `committedBranchRef.current` 5. On confirm: `clearContingencyState()` is called, `committedBranchRef` is updated, branch is committed 6. On cancel: dialog closes, input stays at committed branch #### Standalone Interface No separate standalone mirror is required: the single-file distribution is now `frontend/dist-standalone/standalone.html`, auto-generated from this same React source via `npm run build:standalone` (PR #101). It therefore renders the exact same `ConfirmationDialog` component — the legacy hand-maintained `standalone_interface.html` that used `window.confirm()` has been decommissioned and frozen as `standalone_interface_legacy.html`. --- ## Full State Reset ### When It Happens A complete state reset occurs when: 1. **Load Study** is clicked (or confirmed via dialog) 2. **Apply Settings** is clicked in the Settings modal ### What Gets Reset #### Frontend State | Category | State Variables | Reset Value | |----------|----------------|-------------| | **Diagrams** | `nDiagram`, `n1Diagram`, `actionDiagram`, `originalViewBox` | `null` | | **Analysis** | `result`, `pendingAnalysisResult`, `selectedActionId` | `null` | | **Action tracking** | `selectedActionIds`, `manuallyAddedIds`, `rejectedActionIds` | `new Set()` | | **Loading flags** | `analysisLoading`, `n1Loading`, `actionDiagramLoading` | `false` | | **Analysis flow** | `selectedOverloads`, `monitorDeselected` | `new Set()` / `false` | | **Visualization** | `activeTab`, `actionViewMode`, `vlOverlay` | `'n'` / `'network'` / `null` | | **Branch** | `selectedBranch`, `committedBranchRef`, `inspectQuery` | `''` | | **Messages** | `error`, `infoMessage`, `showMonitoringWarning` | `''` / `false` | | **Refs** | `lastZoomState`, `actionSyncSourceRef` | Reset to defaults | #### What is NOT reset (preserved across reloads) - `networkPath` and `actionPath` (user inputs) - All settings values (`minLineReconnections`, `minCloseCoupling`, etc.) - `linesMonitoringPath` - `monitoringFactor`, `preExistingOverloadThreshold` - `ignoreReconnections`, `pypowsyblFastMode` #### Backend State The `/api/config` endpoint now **always reloads the network** and **resets the recommender service caches**, even if the network path hasn't changed. This prevents: - Stale in-memory network modifications from previous N-1 simulations - Cached simulation environments (`_simulation_env`) from a previous analysis - Leftover `_base_network`, `_last_disconnected_element`, `_dict_action`, `_analysis_context` The `RecommenderService.reset()` method clears, in order: 0. `_reset_model_settings()` — model selection (active model + `compute_overflow_graph` toggle) back to defaults (`ModelSelectionMixin`). 1. Bumps `_prefetch_generation` (so any in-flight NAD-prefetch worker discards its result under the service lock instead of writing into the next study's cache), then `_drain_pending_base_nad_prefetch()` — a no-op when the concurrency lock is present (lock ownership already excludes the worker), a `join` fallback for lock-less test hosts. See [docs/architecture/shared-network-concurrency.md](../architecture/shared-network-concurrency.md). 2. Per-study analysis state: `_last_result`, `_is_running`, `_generator`, `_base_network`, `_simulation_env`, `_last_disconnected_element`, `_dict_action`, `_analysis_context`, `_saved_computed_pairs`. 3. Fast-path caches: `_cached_obs_n`, `_cached_obs_n_id`, `_cached_obs_n1`, `_cached_obs_n1_id`, `_cached_obs_n1_elements`, `_cached_env_context`, `_initial_pst_taps`, `_lf_status_by_variant`, `_last_step1_time`. - `_cached_obs_n1_elements` is the exact contingency element list that produced `_cached_obs_n1` (defense-in-depth on top of the variant-ID check, used by `run_analysis_step1` to decide whether to reuse the pre-warmed obs and skip the contingency LF). See [docs/backend/recommender_models.md § Execution-time breakdown](../backend/recommender_models.md#execution-time-breakdown). - `_last_step1_time` stashes the wall-clock of the most recent `run_analysis_step1` so the step-2 result event can echo it to the React UI for the "Suggestions produced by …" breakdown. 4. `_layout_cache` — the cached `grid_layout.json` DataFrame used as `fixed_positions` for NAD generation. Must be cleared so a new study loaded from a different grid does not reuse the previous grid's substation coordinates. 5. NAD-prefetch state: `_prefetched_base_nad`, `_prefetched_base_nad_error`, `_prefetched_base_nad_event`, `_prefetched_base_nad_thread`. 6. Overflow-graph toggle state: - `_overflow_layout_mode` — reset to `"hierarchical"` so a new study's Overflow Analysis tab always opens in the default layout. - `_overflow_layout_cache` — cleared so file paths produced for the previous contingency cannot be served for the new one. - `_last_step2_context` — the preserved enriched Step-2 context used by `/api/regenerate-overflow-graph` and by the model-swap fast path; stale context must not be reused after a study reload. - `_last_step2_signature` — the input signature `(contingency, selected_overloads, all_overloads, monitor_deselected, additional_lines_to_cut)` that gates the Step-2 overflow-graph cache reuse. Cleared so a freshly loaded study never reuses the previous study's overflow graph. 7. Concurrency lifecycle (D3): `_contingency_variant_lru` — emptied because the contingency variants it tracks die with the Network being dropped. The `_network_lock` / `_study_gate` themselves are NOT cleared — they guard the reset itself and persist for the process. See [docs/architecture/shared-network-concurrency.md](../architecture/shared-network-concurrency.md). Adding a new per-study cache? It MUST be listed in `RecommenderService.reset()` (see `expert_backend/services/recommender_service.py:99`) and, if it holds a thread / future / event, drained first via a `_drain_pending_*` helper — otherwise it WILL leak across studies (regression history: the `_layout_cache` fix on `claude/fix-grid-layout-reset-8TYEV`). ### Why Force Reload? Previously, the backend cached the network path and skipped reloading if it hadn't changed (`if config.network_path != last_network_path`). This caused issues because: 1. N-1 simulations modify the in-memory network (disconnect elements, run load flow) 2. The `RecommenderService` caches simulation environments tied to specific contingencies 3. Action simulations may leave residual state in the recommender's internal caches By always reloading and resetting, we guarantee a clean slate for every new study load. --- ## `hasAnalysisState()` — What Counts as "Analysis State" The function returns `true` if ANY of these are non-null/non-empty: - `result` — analysis results from `run-analysis` - `pendingAnalysisResult` — results waiting to be displayed - `selectedActionId` — an action is being viewed - `actionDiagram` — an action variant diagram is loaded - `manuallyAddedIds.size > 0` — manual simulations were added - `selectedActionIds.size > 0` — actions were favorited - `rejectedActionIds.size > 0` — actions were rejected Note: the mere presence of an N-1 diagram does NOT count as analysis state. Simply selecting a contingency and viewing the N-1 diagram is considered a lightweight operation that doesn't warrant a confirmation dialog. --- ## Testing Tests are in `frontend/src/App.test.tsx` and cover: ### Contingency Change Confirmation (5 tests) 1. No dialog when switching branch without analysis state 2. Dialog shown when switching branch after running analysis 3. State cleared and branch switched on confirm 4. Input reverts to old branch on cancel 5. No dialog triggered for partial/invalid branch text ### Load Study Confirmation (4 tests) 6. Loads directly when no analysis state exists 7. Dialog shown when clicking Load Study after analysis 8. Study reloads on confirm 9. State preserved on cancel Run tests with: ```bash cd frontend npm run test ```