SAFETY-125: a grid charge counts a late meter reading's seconds; the guard row says "over" when it is
checks / unit (pull_request) Successful in 56m44s
infra-pin / fresh (pull_request) Successful in 1s

A meter reading late near a quarter's end left seconds the projection did
not count (SAFETY-99), and charge_headroom_w() read them as room: the cheap
run (and price trading, and the N=2 maintenance charge) went back to full
power in the quarter's last seconds and the quarter could land over a banked
month peak or the cap. peakguard.charge_room_w() counts those seconds at the
held reading for the charge only; the guard's floor is unchanged.

The guard row said "idle, heading for 5 814 W, under this month's 5 413 W
peak". Over the limit at the current rate the row and the month-peak line
now say so, and that the grid charge is cut back.

Version 0.19.2, CHANGELOG, DECISIONS. test_safety125.py new; the lag pins
in test_price_run_fixes.py and test_opt20.py and the AST pin in
test_arbitrage.py follow the change (see the pull request).

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
glenn schrooyen
2026-10-29 02:08:38 +01:00
co-authored by Claude
parent 2932f3af39
commit 88ab8a50e3
13 changed files with 533 additions and 20 deletions
+9 -4
View File
@@ -243,12 +243,16 @@ check("no opposite-direction split on any of these cycles",
# ============================================================================ #
print("\n4. battery modes on, no probe: byte-identical to release/1.0 bf9019e")
# Printed by tools/opt20_scen.py on release/1.0 bf9019e (2026-10-15),
# goodwe-addon-checks:1.
# goodwe-addon-checks:1. SAFETY-125 (0.19.2) moved the two runs that charge
# against the peak aim: the rig's P1 sample is 0.35 s old, and a grid charge
# now counts those seconds, so ch_ch's quarters land on 3450.0 W where they
# landed 1.3-1.4 W over it (bf9019e: 15fc25dd...), and ch_ch_maint's first,
# partial quarter charges a little less at its end (bf9019e: e46e3c66...).
AT_BASE = {
"sf_ch": "422d6578a7decf1ae3749edd1898ff3cd0541d904c95f636cdd9b1de8c1a07c9",
"ch_ch": "15fc25dd34186e56d34b752d37c9e8613295ba737938c92454acca08c83080fb",
"ch_ch": "2b5879ec95810c39a5f0f82c04b9f8e358ea03ddce4ba187cc2b7e0885dfcbfa",
"ch_ch_maint":
"e46e3c666dd63ab33b56e83c0a560c07cec7774a9a70560f55c62a6ae6f55920",
"e56eddabdb785f81793e7afa525f8e6da237b0b2165860aa8e0f8a6499b275a9",
}
_got = sc.no_probe_digests()
for _k in AT_BASE:
@@ -256,7 +260,8 @@ for _k in AT_BASE:
f"{_got[_k][:16]}")
check("with no refusal every share, grid, trade sentence, role and plant "
"power of a scripted cheap run equals bf9019e's (A Solar first, A "
"charging, A in maintenance charge)", _got == AT_BASE)
"charging, A in maintenance charge; the last two as SAFETY-125 moved "
"them)", _got == AT_BASE)
print()
if fails: