# EMS planner bug triage — reference (MCP)

Všechno jen **read-only** `SELECT`. Server: **`user-postgres-ems`**, nástroj **`query`**.

Seznam lokalit a `fn_plan_explain_bundle` → [ems-plan-explain/reference.md](../ems-plan-explain/reference.md).

---

## 1) Bug typy (klasifikace)

| Typ | Popis | Typické flags / signály | Fix větev |
|-----|--------|-------------------------|-----------|
| **A** | Degradovaný solve — Infeasible retry krok 3+ | `any_relaxed_solve`, `relaxed_neg_prep_window`, `evening_push_hard_suppressed` | Branch 1: granulární relaxace + failed-run journal |
| **B** | Večerní export chybí před neg dnem | Vysoké SoC ve špičce, `grid_setpoint_w > 0` @ ~5 Kč buy, `neg_evening_push_ts` prázdné, zítra `buy<0` / vysoká FVE | Branch 2: neg-večer k `reserve_soc` |
| **C** | Fixní tarif — špatné nabíjení / žádný večerní push | `evening_push_ts: []`, SoC ~60–80 % ve slunci, v58 `bc_pv=0` | Branch 3: charge-slot-budget |
| **D** | Provoz / exekuce, ne LP | `mode != AUTO`, starý `active` run, ruční MANUAL | Návrat do AUTO, ruční replan po fixu |
| **E** | GEN port / pole B při sell&lt;0 | BA81 + `deye_gen_microinverter_cutoff`, audit export v sell&lt;0 | Branch 4: cutoff exekuce |

---

## 2) MCP — aktivní run + relax flags

Nahraď `$site_id` (např. 2 = home-01):

```sql
select pr.id,
       pr.created_at,
       pr.run_type,
       pr.triggered_by,
       pr.soc_at_replan_wh,
       pr.solver_params->'inputs'->>'any_relaxed_solve' as relaxed,
       pr.solver_params->'inputs'->>'relaxed_expensive_import' as r_exp_import,
       pr.solver_params->'inputs'->>'relaxed_neg_buy_charge' as r_neg_buy,
       pr.solver_params->'inputs'->>'relaxed_neg_prep_window' as r_neg_prep,
       pr.solver_params->'inputs'->>'neg_sell_phases_fallback' as r_phases_off,
       pr.solver_params->'inputs'->>'evening_push_hard_suppressed' as push_suppressed,
       pr.solver_params->'inputs'->>'pre_neg_pv_export_forecast_ok' as pre_neg_ok,
       pr.solver_params->'inputs'->>'neg_evening_push_ts' as neg_eve_push,
       pr.solver_params->'inputs'->>'evening_push_ts' as evening_push,
       pr.solver_params->'inputs'->>'charge_acquisition_buy_czk_kwh' as acq,
       pr.solver_params->'inputs'->>'neg_sell_day_pv_usable_wh' as neg_pv_wh,
       pr.solver_params->>'planner_build_tag' as tag
  from ems.planning_run pr
 where pr.site_id = $site_id
   and pr.status = 'active'
 order by pr.created_at desc
 limit 1;
```

Poslední běhy (včetně comparison) za 48 h:

```sql
select pr.id, pr.status, pr.run_type, pr.created_at,
       pr.solver_params->'inputs'->>'relaxed_neg_prep_window' as r3,
       pr.solver_params->'inputs'->>'evening_push_hard_suppressed' as push_sup
  from ems.planning_run pr
 where pr.site_id = $site_id
   and pr.created_at >= now() - interval '48 hours'
 order by pr.created_at desc
 limit 20;
```

---

## 3) Site archetypy (orientace)

| | **home-01** | **BA81** | **KV1** |
|---|-------------|----------|---------|
| Buy | spot | fixed ~2,55 NT | fixed 5,25 |
| `block_export_on_negative_sell` | false | false | **true** |
| Neg sell fáze | 80 % prep | default | **100 % (off)** |
| `deye_gen_microinverter_cutoff` | false | **true** | false |
| `planner_terminal_soc_value_factor` | **0,9** | 0,2 | 0,2 |
| Typický večerní bug | A + B (relaxed + drží SoC) | C (no evening_push) | občas A, jinak OK |

Konfigurace z DB:

```sql
select s.code,
       smc.purchase_pricing_mode,
       sgc.block_export_on_negative_sell,
       ai.deye_gen_microinverter_cutoff_enabled,
       ab.reserve_soc_percent,
       ab.min_soc_percent,
       ab.planner_neg_sell_prep_soc_percent,
       ab.planner_terminal_soc_value_factor
  from ems.site s
  left join ems.site_market_config smc
    on smc.site_id = s.id and smc.valid_to is null
  left join ems.site_grid_connection sgc on sgc.site_id = s.id
  left join ems.asset_inverter ai
    on ai.site_id = s.id and ai.code = 'deye-main'
  left join ems.asset_battery ab
    on ab.site_id = s.id and ab.code = 'bat-main'
 where s.code in ('home-01', 'BA81', 'KV1');
```

---

## 4) Provozní režim a log

```sql
select mode_code from ems.site_operating_mode where site_id = $site_id;

select activated_at at time zone 'Europe/Prague' as ts_prague,
       mode_code,
       activated_by
  from ems.site_operating_mode_log
 where site_id = $site_id
 order by activated_at desc
 limit 8;
```

Rolling replan běží jen v **AUTO**.

---

## 5) Večerní okno — planning_interval

```sql
select pi.interval_start at time zone 'Europe/Prague' as slot_prague,
       pi.battery_setpoint_w,
       pi.grid_setpoint_w,
       pi.battery_soc_target_pct,
       pi.effective_buy_price,
       pi.effective_sell_price
  from ems.planning_interval pi
 where pi.run_id = (
         select id from ems.planning_run
          where site_id = $site_id and status = 'active'
          order by created_at desc limit 1
       )
   and pi.interval_start >= (current_timestamp at time zone 'Europe/Prague')::date
       + interval '17 hours'
   and pi.interval_start < (current_timestamp at time zone 'Europe/Prague')::date
       + interval '1 day' + interval '6 hours'
 order by pi.interval_start;
```

**Červená vlajka:** `battery_setpoint_w = 0` a `grid_setpoint_w > 0` při sell ~3 Kč a SoC &gt; reserve — import místo exportu baterie.

---

## 6) Zítřejší neg ceny

```sql
select interval_start at time zone 'Europe/Prague' as slot_prague,
       effective_buy_price_czk_kwh as buy,
       effective_sell_price_czk_kwh as sell
  from ems.vw_site_effective_price
 where site_id = $site_id
   and interval_start >= (current_timestamp at time zone 'Europe/Prague')::date
       + interval '1 day'
   and interval_start < (current_timestamp at time zone 'Europe/Prague')::date
       + interval '2 days'
   and (effective_buy_price_czk_kwh < 0 or effective_sell_price_czk_kwh < 0)
 order by interval_start;
```

---

## 7) Slovník solver_params.inputs (výběr)

| Klíč | Význam |
|------|--------|
| `evening_push_hard_suppressed` | true = **bez** tvrdého `ge_bat` push (typicky retry krok 3) |
| `relaxed_neg_prep_window` | Vypnuty neg-evening kotvy, prep hold, neg evening push |
| `pre_neg_pv_export_forecast_ok` | Cushion FVE v sell&lt;0 okně — false → nemá exportovat ranní PV před neg |
| `neg_evening_push_ts` | Sloty D−1 večer pro vývoj před neg oknem |
| `charge_acquisition_buy_czk_kwh` | Účinná cena energie v baterii pro arbitráž exportu |
| `neg_sell_day_pv_usable_wh` | Forecast Wh do baterie v sell&lt;0 okně zítřka |
| `morning_pre_neg_export_hard` | Tvrdý ranní export před sell&lt;0 |

Plný snap: `select ems.fn_planning_run_debug(<run_id>);`

---

## 8) Fix větve (implementační plán v repu)

| Branch | Obsah | Priorita |
|--------|--------|----------|
| **1** | Failed-run journal, bisect Infeasible, granulární relaxace | P0 |
| **2** | home-01 neg-večer → `reserve_soc`, oddělit push od prep relax | P0 |
| **3** | charge-slot-budget v R__063, BA81/KV1 večerní export | P1 |
| **4** | BA81 GEN cutoff audit | P1 |
| **5** | Dynamický terminal SoC při future neg buy | P2 |

Detail: plán v `.cursor/plans/` nebo `docs/planning-changelog.md` + `docs/04-modules/planning-charge-slot-budget.md`.

---

## 9) Ověření po fixu

```bash
pytest backend/tests/test_planning_dispatch_milp.py -k "evening or NegSell or Infeasible"
```

```bash
python scripts/diagnose_home01_infeasible.py
```

MCP po deployi — nový active run **bez** `relaxed_neg_prep_window` nebo s `evening_push_hard_suppressed: false` a večerní sloty s `grid_setpoint_w < 0`.
