SAFETY-79 review: margin under a banked month peak too; identity harness reaches headroom and maintenance
- quarter_target_w() is now the margin under quarter_ceiling_w(): a peak
banked between cap - margin and the cap was aimed at exactly, so one
overshoot crossed the cap; above the cap the same overshoot raised the bill.
- safety79_identity.py hashes charge_headroom_w() and the shaping suspension,
and adds n1_maint/n2_maint (cheap window, forecast across cap - 50 .. cap).
Digests re-printed on ed739d4 and pinned; margin 0 still identical.
- test_margin_under_banked_peak; DECISIONS and DOCS updated.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
e6096d3e17
commit
ce0aea30b6
@@ -47,20 +47,25 @@ CAP = float(rig.CAP_W)
|
||||
print("1. peak_margin_w 0: byte-identical to release/1.0 ed739d4")
|
||||
# Printed by `python3 tools/safety79_identity.py 0` on the ed739d4 tree (the
|
||||
# tool copied in, nothing else changed; that tree ignores the key), in the CI
|
||||
# image, 2026-09-28. 540 cycles each: every command, the loop and arbiter
|
||||
# sentences, the allocation, and the guard's floor, limit and sentence.
|
||||
# image; re-printed 2026-09-29 (review) after adding the charge headroom, the
|
||||
# shaping suspension and the two maintenance scenarios. 540 cycles each.
|
||||
ED739D4 = {
|
||||
"n1": "36f2dc6cd82ed63b93125dc3f8ea5f475e494255c614a4d2f353dad06e44f884",
|
||||
"n2": "1d388daf1bcfc3e16b1ef696377ae2a01831963374e2a6a4d2f998b5c6d997c2",
|
||||
"n1_off": "76e41db45667fffebd3ce99a49873a31d0b86fcc0c520057bbf38cc4f27138ca",
|
||||
"n2_off": "196653957e2fdf227daa42447ac170c45292e6c3f4184196dd99c1140359e415",
|
||||
"n1": "82316f7d4362fae4c035a1bed5e7907cf362faba994c634d0b1c356c501f53e5",
|
||||
"n2": "0dc24e16a8390ec42edacf735eaf20273847b33b9c67d7cdc536df0ca739e421",
|
||||
"n1_off": "eed44f058f3fcc6e7651c5c84c6fa6313e7520035ad7cac282012cf6909668a0",
|
||||
"n2_off": "69796c107b9f78892e04d282b754f889590acf9fe1b96f09525f5ea2005e9c0a",
|
||||
"n1_maint":
|
||||
"bd4a88a10aa840a360ac781ac04e30b040a5aac5426fabba1fb259d71dfeba67",
|
||||
"n2_maint":
|
||||
"0656bc5a4a2fa3a7455822fdd7da5f68f57fd9593f374e869a0d30ba7e429275",
|
||||
}
|
||||
_zero = rig.digests(0)
|
||||
_default = rig.digests()
|
||||
for k in ED739D4:
|
||||
print(f" {k:7} margin 0 {'same' if _zero[k] == ED739D4[k] else 'DIFFERS'}"
|
||||
f" {_zero[k][:16]}, default {_default[k][:16]}")
|
||||
check("⭐ at N=1 and N=2, with and without battery modes, margin 0 is "
|
||||
check("⭐ at N=1 and N=2, with and without battery modes and with a "
|
||||
"maintenance charge, margin 0 is "
|
||||
"byte-identical to ed739d4", _zero == ED739D4)
|
||||
check("...and the default (key absent) changes every scenario, so the "
|
||||
"scenarios do reach the margin", all(_default[k] != ED739D4[k]
|
||||
@@ -118,15 +123,42 @@ check("test_off_battery_n2: at N=2 with B Off and A at its rating, B covers "
|
||||
_c0["b"] == 1000.0 and _g0 == CAP
|
||||
and _c50["b"] == 1050.0 and _g50 == CAP - 50)
|
||||
|
||||
_obs = type("R", (), {"is_observed": True, "peak_w": 2480.0})()
|
||||
check("test_month_peak_not_lowered: a month peak already paid for is aimed "
|
||||
"at as it stands (2480 W with cap 2500, margin 50), and a higher one "
|
||||
"too; no peak aims at cap - margin",
|
||||
quarter_target_w(CAP, 50, _obs) == 2480.0
|
||||
and quarter_target_w(CAP, 50,
|
||||
type("R", (), {"is_observed": True,
|
||||
"peak_w": 3000.0})()) == 3000.0
|
||||
and quarter_target_w(CAP, 50, None) == CAP - 50)
|
||||
def _peak(w, observed=True):
|
||||
return type("R", (), {"is_observed": observed, "peak_w": w})()
|
||||
|
||||
|
||||
# Review (SAFETY-79 point 2): the margin applies under a month peak already
|
||||
# banked, too. Aiming AT it can bank a few W over it, which raises the bill;
|
||||
# and a banked peak between cap - margin and the cap (a quarter the old guard
|
||||
# held at the cap, say) was aimed at exactly, so one overshoot crossed the cap.
|
||||
check("test_margin_under_banked_peak: the aim is the margin under the limit "
|
||||
"that holds - the cap, or a month peak already banked above it",
|
||||
quarter_target_w(CAP, 50, _peak(2499.0)) == CAP - 50
|
||||
and quarter_target_w(CAP, 50, _peak(3000.0)) == 2950.0
|
||||
and quarter_target_w(CAP, 50, _peak(3000.0, observed=False)) == CAP - 50
|
||||
and quarter_target_w(CAP, 50, None) == CAP - 50
|
||||
and quarter_target_w(CAP, 0, _peak(3000.0)) == 3000.0)
|
||||
|
||||
|
||||
def _decide(banked_w, projected_w):
|
||||
"""Half a quarter at `projected_w`, battery idle, a month peak banked."""
|
||||
from datetime import datetime, timezone
|
||||
from app.tariff import Projection
|
||||
pr = Projection(projected_w, projected_w, 450.0, 1.0, False,
|
||||
datetime(2026, 9, 1, 12, 0, tzinfo=timezone.utc),
|
||||
projected_w)
|
||||
return peakguard.decide(pr, batt_w=0.0, cap_w=CAP, margin_w=50,
|
||||
reading=_peak(banked_w), achievable_w=5000.0,
|
||||
soc=60.0, soc_floor=20.0)
|
||||
|
||||
|
||||
_d1, _d2 = _decide(2499.0, 2600.0), _decide(3000.0, 3100.0)
|
||||
print(f" banked 2499 W: floor {_d1.floor_w}, limit {_d1.ceiling_w}; "
|
||||
f"banked 3000 W: floor {_d2.floor_w}, limit {_d2.ceiling_w}")
|
||||
check("...and decide() sizes the floor to land the quarter 50 W under it "
|
||||
"(2449 and 2950 W), while the reported limit stays the cap or the peak",
|
||||
_d1.floor_w == 2 * (2600.0 - 2450.0) and _d1.ceiling_w == CAP
|
||||
and _d2.floor_w == 2 * (3100.0 - 2950.0) and _d2.ceiling_w == 3000.0)
|
||||
|
||||
# ============================================================================ #
|
||||
print("\n3. the bill and the reports keep the real cap")
|
||||
@@ -139,7 +171,7 @@ check("test_billing_uses_cap: the month-peak tracker (bill, headroom, 80 % "
|
||||
"warning) is built on the real cap, not cap - margin",
|
||||
_c.peak.cap_w == CAP and _c.peak.warning_frac() == _pk / CAP
|
||||
and _c.peak.headroom_w() == CAP - _pk)
|
||||
_eng = [g for r in _runs["n1_off", None][0] for g in (r[-1],)
|
||||
_eng = [g for r in _runs["n1_off", None][0] for g in (r[-1][1],)
|
||||
if g and g[0] and g[1]]
|
||||
print(f" {len(_eng)} engaged cycles; one: {_eng[0][4] if _eng else None}")
|
||||
check("test_reports_use_cap: every engaged sentence names the real limit and "
|
||||
|
||||
Reference in New Issue
Block a user