Skip to content

Retire planner_cheap and planner_self, and the cheap_charge MPC mode behind them #802

Description

@frahlg

planner_cheap and planner_self are superseded but still shipping. Both mode
namespaces still carry them, and every surface that touches modes has to know
about all four planner variants when only two are ever offered.

What is already true

mpc.ModeCheapCharge says so itself, in go/internal/mpc/mpc.go:

Superseded by ModePassiveArbitrage as of v0.82 — the merged mode covers the
same use case (charge from cheapest available source, never export battery)
and removes the operator-facing choice between "smart self-consumption" and
"cheap charge" that the planner is already capable of making on its own.
Kept here for existing-config compatibility.

And ModeCatalog() has already moved planner_self and planner_cheap to
TierHidden, so neither is reachable from the dashboard. What is left is the
code, the config surface and the golden corpus.

Why it is worth finishing

Two namespaces map 1:1 for the planner modes — control.ModePlannerCheap
mpc.ModeCheapCharge, and so on through PlannerMPCMode(). That indirection
earns its keep for the six non-planner modes, which have no MPC counterpart at
all, but for the planner ones it is four names for two behaviours.

It also leaks outward. Anything that renders or validates modes carries the
full list, and the FTW webapp now derives its choices from ModeCatalog()
over the wire — so a mode that exists only for config compatibility still costs
a row in every client that speaks to a box.

Roughly where it lives

rg 'planner_cheap|ModePlannerCheap|cheap_charge|ModeCheapCharge|planner_self|ModePlannerSelf' finds it in:

  • go/internal/control/dispatch.go — the enum, AllModes(), PlannerMPCMode(), IsPlannerMode()
  • go/internal/mpc/mpc.go, service.go, shadow_evaluator.go
  • go/internal/config/config.go — the config surface that is the stated reason for keeping them
  • go/internal/control/modes_catalog.go
  • optimizer/ftw_optimizer/model.py, multistage.py, recourse.py, direct_highs.py, backtest.py
  • web/plan.js, web/settings/tabs/planner.js, web/settings/tabs/control.js
  • the golden corpus in go/internal/control/testdata/golden/ — several hundred references

What removing them needs

  1. A migration for existing configs. This is the whole reason they are still
    here. A site on planner_cheap should land on planner_passive_arbitrage;
    planner_self needs a decision, since passive arbitrage may grid-charge and
    planner_self explicitly may not. That difference is real for anyone who
    chose it deliberately, and it should be checked against how many sites
    actually run it before being collapsed.
  2. Regenerating the golden corpus, which is where most of the diff will be.
  3. Deciding whether mpc.Mode stays a separate type at all once the planner
    modes are 1:1. Probably yes — the six non-planner modes justify it — but
    worth stating rather than inheriting.

What this is not

Not urgent, and not a bug. Nothing is broken; there is dead surface area that
grows a little every time something new has to enumerate modes.

Raised while building the FTW webapp, which now reads ModeCatalog() from the
box rather than hard-coding a list — the same pattern web/app.js already
follows.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions