Core owns safety. Drivers translate hardware and the optimizer proposes plans, but neither may bypass the final validation and dispatch path.
All core values use the site convention: positive W flows into the site and negative W flows out. Only a driver converts vendor signs. A sign fix above the driver boundary is almost always hiding a driver bug. See site-convention.md.
The telemetry watchdog runs every control tick. A driver with no fresh
successful reading within site.watchdog_timeout_s is marked offline and sent
its autonomous driver_default_mode.
The configured site meter is stricter: stale meter data stops all storage and loadpoint dispatch. If the meter exposes per-phase current, every configured phase must remain fresh as well because those readings are required to protect the site fuse. Core sends connected loadpoints an explicit zero-power standdown and reverts controllable drivers to autonomous mode once on the stale transition, including a combined site-meter/battery owner. Telemetry observation and schedule state continue advancing while actuation is blocked; normal dispatch resumes only after the required readings recover.
Source and executable specification:
go/internal/telemetry- the control tick in
go/cmd/ftw/main.go - watchdog and stale-meter tests beside those packages
Safety constraints are applied after the requested mode or planner target has been computed. The final command path enforces:
- driver and aggregate charge/discharge capability;
- configured battery SoC floor and ceiling;
- whole-site import/export and fuse capacity;
- per-phase current when phase telemetry exists;
- dispatch interval and normal slew rate;
- charge-only reactive correction where a plan would otherwise create unintended import;
- EV/loadpoint fuse sharing and surplus-only promises.
The reactive fuse saver may bypass normal slew limiting because preventing a fuse trip is the higher-priority quantified risk. It must still respect available battery energy and hardware power capability.
The EV hard limit treats the main fuse as heat, not as one reading. It
low-passes each phase's I² from the site meter with a 60 s time constant.
Each phase's working limit is the fuse rating less fuse.safety_margin_a;
no amps are held back beyond that setting. A phase over the limit cuts the
EVs once that filtered current passes the same limit, and at once above
1.2 × the fuse. A running car rises one step, or the rest of its demand,
only when that step fits under the limit, and not within 20 s of a cut. Its
rise reads the higher of the reading and that filtered current. An idle
car's start reads each phase as it is, plus its new current; the filtered
current holds it back only while it is over the limit. Neither counts
battery discharge that the fuse guard added as room: that relief ends once
the site fits without it. Where the meter
reports phase currents, they replace total import as the EVs' fuse check; a
tariff import ceiling still applies.
A phase's current takes its direction from the same phase's power. It counts as export only when that power shows it clearly and the phases' powers add up to the meter's total; otherwise it counts as import. The battery's fuse checks make one exception for a meter with no phase power while the total exports: a phase counts as export when the other phases' currents cannot carry that export alone. An exporting phase is never cut for: less EV load would raise its export. A rise or start on it reads its current as it is, without its heat.
The implementation and tests in go/internal/control
are authoritative. Planner-side limits in go/internal/mpc
reduce infeasible plans but do not
replace runtime enforcement.
Optimizer output is untrusted input. Core requires a complete, time-aligned, finite trajectory within the request contract. Invalid, partial, late or unavailable output is rejected and the Go fallback remains available.
Learned battery, PV and load models are advisory. Confidence gates and physical sanity envelopes prevent a young or drifting model from overriding direct measurement or configured bounds. Model saturation must be calculated from physical capability, never from a previously clamped command; feeding a clamp back into model capacity creates a self-reinforcing loss of authority.
Every controllable driver implements a safe autonomous mode appropriate to the device, normally vendor self-consumption or cancellation of forced power. It is used on watchdog transition, shutdown, driver removal and relevant reloads. Default mode must not depend on core continuing to send commands.
Do not add a “just in case” clamp. Each protection must name:
- the measurable unsafe condition;
- the physical/configured limit;
- the safe reaction and recovery condition;
- its interaction with higher-priority protections;
- tests for both activation and non-activation.
Do not remove or reorder a protection based only on a simplified happy-path simulation. Verify stale data, phase imbalance, empty/full assets, planner failure and recovery.
Home Assistant, notifications, cloud drivers, price/weather services and Nova fail soft. Their network I/O is outside the control tick, and their failure cannot disable local measurement or safety. Updates install immutable, checksummed release packages, and a release that does not become ready falls back to the previous one.