The probe
Swapping EKF for UKF is v6 on the roadmap and the cleanest test of the platform's one rule: a change in A must not require a change in B. It fails today.
What is welded, with evidence
| coupling |
where |
what a swap forces |
| Hardcoded class |
core/simulator/core.py:44 import, :82 ekf = ExtendedKalmanFilter(bp, tp, ekf_p, thp, elp) |
edit the simulator core |
| Config type in the signature |
core/simulator/core.py:65 ekf_p: EKFParams — one of 15 positional params |
edit run_simulation and its 7 call sites |
| Constructor takes 5 param objects |
core/estimators/ekf.py:47 |
every estimator must accept the same five, or the core changes again |
| Algorithm name in the trace schema |
traces.soc_ekf / soh_ekf / temp_ekf / vrc1_ekf (core/simulator/traces.py:53-56), record_mpc(ekf_state=…) |
ledger, plots and traces say "ekf" for a UKF — or rename in 4 files |
| Comments and warnings |
core/mpc/economic.py:70,263,296, core/planners/stochastic_ems.py:125, strategies/ems_economic_mpc/strategy.py:5 |
cosmetic, but drift compounds |
Files touched for a UKF today: 6 minimum, ~10 honestly. For a change that should be one new file plus one line naming it.
What already works — the contract exists, unstated
The loop reaches the estimator through exactly two calls: ekf.get_estimate() (core.py:96) and ekf.step(u, y_meas) (core.py:164). A UKF exposing those two is a drop-in at the call site. The port is there; nothing declares it.
The pattern is one seam over
core/simulator/strategy.py already does this right: Strategy is a frozen dataclass naming a planner and an optional MPC through _PlannerLike / _MPCLike Protocols; the loop calls strategy.planner.solve() and never branches. v5b was added as a new folder with zero core edits, and tests/test_greek_milp_bidding_e2e.py::test_v5_strategy_not_affected_by_phase3 proves the old traces stayed bit-identical. The estimator is exactly what did not get that treatment.
Done when
Phase 1 of the platform epic. Depends on the layout issue (#1).
The probe
Swapping EKF for UKF is v6 on the roadmap and the cleanest test of the platform's one rule: a change in A must not require a change in B. It fails today.
What is welded, with evidence
core/simulator/core.py:44import,:82ekf = ExtendedKalmanFilter(bp, tp, ekf_p, thp, elp)core/simulator/core.py:65ekf_p: EKFParams— one of 15 positional paramsrun_simulationand its 7 call sitescore/estimators/ekf.py:47traces.soc_ekf / soh_ekf / temp_ekf / vrc1_ekf(core/simulator/traces.py:53-56),record_mpc(ekf_state=…)core/mpc/economic.py:70,263,296,core/planners/stochastic_ems.py:125,strategies/ems_economic_mpc/strategy.py:5Files touched for a UKF today: 6 minimum, ~10 honestly. For a change that should be one new file plus one line naming it.
What already works — the contract exists, unstated
The loop reaches the estimator through exactly two calls:
ekf.get_estimate()(core.py:96) andekf.step(u, y_meas)(core.py:164). A UKF exposing those two is a drop-in at the call site. The port is there; nothing declares it.The pattern is one seam over
core/simulator/strategy.pyalready does this right:Strategyis a frozen dataclass naming a planner and an optional MPC through_PlannerLike/_MPCLikeProtocols; the loop callsstrategy.planner.solve()and never branches. v5b was added as a new folder with zero core edits, andtests/test_greek_milp_bidding_e2e.py::test_v5_strategy_not_affected_by_phase3proves the old traces stayed bit-identical. The estimator is exactly what did not get that treatment.Done when
EstimatorLikeProtocol (step,get_estimate) owned by the core, next to_PlannerLike.Strategy.estimatoror a registry entry — never inside the loop.run_simulation's signature.soc_est), not the algorithm.Phase 1 of the platform epic. Depends on the layout issue (#1).