At N=1 the drain is the site SET at P_MAINTENANCE (60), so the peak floor
(P_PEAK, 50) was not applied and the house imported what the drain left.
Where the floor asks for more than the drain rate, main.py now refiles the
peak claim at P_MAINTENANCE, so it wins; safety SETs still park everything.
Nothing else changes: safety78_identity --diff against 35cdffe changes only
cycles where the guard wanted more than the drain; N=2 is byte-identical.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2.0 KiB
SAFETY-83 - the peak guard outranks the maintenance drain at N=1
Status: DECIDED 2026-09-29 (owner go, P1). Follows SAFETY-78's "Not
changed" list. Tests: test_safety83.py.
The defect. At N=1 the drain is the site SET at P_MAINTENANCE (60). The
peak floor is a limit at P_PEAK (50), and clause 2 applies only limits at or
above the winner, so the floor was ignored and the house imported what the
drain left. A 5000 W house under a 2500 W limit and a 2000 W drain banked
3000 W quarters; on the owner's settings (limit 3500, drain 2500, margin 50) a
breach starts at about 5950 W.
The fix. While a drain owns the setpoint at N=1 and the peak floor asks
for more than the drain rate, main.py (_peak_over_drain()) refiles that
peak claim at P_MAINTENANCE. The arbiter then applies it: the target is the
floor, still inside the safety limits, and a safety SET (100) still parks
everything. Where the floor is at or below the drain rate nothing changes.
- The order in effect is now the same at N=1 and N=2: safety limits and the
SAFETY-73 refusal > peak guard (with the SAFETY-79 margin) > maintenance >
modes > trade > loop (
arbiter.py, beside the priorities). - The drain keeps counting: the battery discharges at least the drain rate on every cycle, so the cycle is not reset or stalled; it only ends sooner.
- Below the drain rate the rest is still exported (owner, 2026-09-29).
- Identity:
tools/safety78_identity.py --diffagainst35cdffechanges only cycles where the guard asked for more than the drain; after such an episode the commands are identical again and only the log and the idle loop's integrator differ. N=2 is unchanged.
Not changed.
- Between the drain rate and the peak aim, the house still imports what the drain leaves (N=1 behaviour kept by the identity). At N=2 the loop covers it.
- A maintenance HOLD (0 W, 120 min, battery full) still outranks the peak guard at N=1. Discharging there spends the charge the BMS balances on; it is an owner question, not this fix.