--- name: ems-planner-bug-triage description: >- Triages EMS planner bugs from live Postgres (MCP): degraded Infeasible retry chain, evening export vs battery hold, neg-buy/neg-sell days, fixed-tariff sites (BA81/KV1), failed replan vs stale active plan. Use when the user reports planner errors, wrong evening export, battery held while buy ~5 Kč/kWh, relaxed_neg_prep_window, evening_push_hard_suppressed, or multi-site planner comparison. Complements ems-plan-explain — use this skill for incident/bug classification and fix-branch hints. --- # EMS — triáž bugů plánovače ## Kdy použít (vs. ems-plan-explain) | Situace | Skill | |---------|--------| | „Proč v tom slotu nabíjí / neexportuje?“ | [ems-plan-explain](../ems-plan-explain/SKILL.md) | | „Plánovač blbne / neprodává večer / 422 Infeasible / BA81 vs home-01“ | **tento skill** | | Degradovaný plán (relaxed solve) ale run `active` | **tento skill** | | Porovnání více lokalit uživatel **explicitně** jmenoval | **tento skill** (jinak jedna site) | Typické spouštěče: *neprodává večer*, *drží baterku @ 5 Kč*, *nepřeplánovalo po OTE*, *Solver: Infeasible*, *evening_push prázdný*, *sell<0 a výroba do site*. ## Tvrdá pravidla 1. **MCP first** — server `user-postgres-ems`, nástroj `query`, jen `SELECT`. Po chybě: přesný text + VPN/MCP zapnutý. 2. **Selhání plánu není v `planning_run`** — status je jen `active` / `superseded` / `comparison` / `draft`. API **422** a scheduler exception = logy backendu; aktivní run může být **starý** nebo **degradovaný**. 3. Rozliš: **plán v DB** vs **exekuce** — `site_operating_mode != AUTO` → rolling replan se **přeskakuje** (ne nutně solver bug). 4. **Dvě podlahy SoC:** export / strategie → **`reserve_soc_percent`** (~20 %); dům v noci (Deye PASSIVE) → může jít k **`min_soc_percent`** (~10 %). Před návrhem večerního vývoje se u exportu domluv **`reserve`**, ne `min_soc`, pokud uživatel neřekne jinak. 5. **`neg sell` ≠ `buy < 0`:** vyprázdnit před **`sell < 0`** (headroom FVE) není totéž co strategie před **`buy < 0`** (levný import). ## Checklist triáže ``` - [ ] site_id (kód / id / výběr ze seznamu — viz ems-plan-explain reference §0) - [ ] site_operating_mode + poslední řádky site_operating_mode_log - [ ] active planning_run: id, created_at, run_type, triggered_by, soc_at_replan_wh - [ ] solver_params.inputs: relaxed_*, evening_push_hard_suppressed, neg_evening_*, pre_neg_pv_export_forecast_ok, charge_acquisition_buy_czk_kwh - [ ] Večerní okno: planning_interval (battery/grid setpoint, soc target, ceny) - [ ] Zítra: neg sloty ve vw_site_effective_price; snap neg_sell_day_pv_usable_wh - [ ] Klasifikace bug typu A–E (reference.md §1) - [ ] Návrh fix větve + ověření po fixu ``` ## Rozhodovací strom ``` Replan 422 / žádný nový run po OTE? ├─ mode != AUTO → provozní (MANUAL/SELF_SUSTAIN), plán zastaralý ├─ poslední run any_relaxed + evening_push_hard_suppressed │ └─ BUG typ A/B: degradovaný solve — Branch 1 + 2 └─ žádný řádek v planning_run → backend log „Infeasible“ (Branch 1) Večer neprodává, drží vysoké SoC? ├─ evening_push_hard_suppressed = true → tvrdý push vypnutý (Branch 2) ├─ grid import @ drahý buy + vysoké SoC → terminal SoC + pos_sell_pre_neg_buy (Branch 2/5) ├─ zítra buy<0 + vysoká FVE, pre_neg ok = false │ └─ měl jít večer k reserve, ne držet ~80 % (Branch 2) └─ fixed tarif + evening_push_ts = [] → v58 / charge-slot-budget (Branch 3) BA81: sell<0, výroba „do site“ / export? └─ deye_gen_microinverter_cutoff + pole B — exekuce GEN (Branch 4) ``` ## Retry řetězec (solve_dispatch) Při Infeasible solver postupně zapíná (viz `planning_engine.py` ~4216): 1. `relaxed_expensive_import` 2. `relaxed_neg_buy_charge` 3. `relaxed_neg_prep_window` → **vypne** neg-evening push, kotvy, prep hold; **`evening_push_hard_suppressed = true`** 4. `neg_sell_phases_fallback` (prep_soc = 100 %) **Symptom degradace:** run `active`, ale ve špičce **import ze sítě** místo exportu baterie; `neg_evening_push_ts` prázdné; plán drží SoC nad očekávání před neg dnem. ## Výstup pro uživatele (šablona) 1. **Fakta z DB** — run id, čas, mode, 3–5 klíčových flags, 2–3 večerní sloty (W, SoC %, buy/sell). 2. **Root cause** — jedna věta: degradovaný retry / konfigurace site / režim / exekuce. 3. **Bug typ** — A–E z [reference.md](reference.md). 4. **Doporučená větev opravy** — Branch 1–5 + soubor v repu. 5. **Ověření** — MCP dotaz nebo `pytest backend/tests/test_planning_dispatch_milp.py -k "…"`. ## Kód a dokumentace | Téma | Soubor | |------|--------| | LP + retry | `backend/services/planning_engine.py` — `solve_dispatch` | | `evening_push_hard_suppressed` | ~2810 | | `pos_sell_pre_neg_buy` → `ge=0` | ~3929 | | SQL masky | `db/routines/R__063_fn_load_planning_slots_full.sql` | | Charge-slot budget (plán) | `docs/04-modules/planning-charge-slot-budget.md` | | Changelog v55–v57 | `docs/planning-changelog.md` | | Bisect fixture | `scripts/diagnose_home01_infeasible.py` | ## SQL a archetypy site → [reference.md](reference.md) ## Anti-patterns - Diagnostikovat Infeasible **bez** `solver_params.inputs` z posledního úspěšného runu — může být právě ten degradovaný. - Zaměnit „plán neexportuje“ s „exporter neběží“ — v MANUAL/SELF_SUSTAIN se plán nemusí aktualizovat. - U fixního tarifu očekávat stejné `evening_push_ts` jako u home-01 — BA81/KV1 mají jinou větev (viz reference §3).