SAFETY-87: grade the surviving mutants (dropped battery, own-floor naming, pair permission, latched row)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019DBUfvndgHwaV339Fvd2gr
This commit is contained in:
@@ -33,6 +33,7 @@ import json
|
||||
import logging
|
||||
import os
|
||||
import sys
|
||||
import types
|
||||
|
||||
HERE = os.path.dirname(os.path.abspath(__file__))
|
||||
sys.path.insert(0, HERE)
|
||||
@@ -393,6 +394,33 @@ check("...and at N=1 the floor-limited claim is unchanged: 0 W, 'SoC floor'",
|
||||
peakguard.claim(_lim).lo == 0.0
|
||||
and peakguard.claim(_lim).reason == "SoC floor")
|
||||
|
||||
|
||||
|
||||
def three(socs, cuts):
|
||||
"""`_peak_on_able()` on a stub roster of three (SIMULATED packs)."""
|
||||
names = ("x", "y", "z")
|
||||
invs = [types.SimpleNamespace(name=n, refused=0, rating_w=lambda: 2500.0)
|
||||
for n in names]
|
||||
stub = types.SimpleNamespace(
|
||||
inverters=invs, o={"peak_soc_floor": 20},
|
||||
packs=[peakguard.Pack(n, so, c) for n, so, c in zip(names, socs, cuts)],
|
||||
live_units=lambda: names)
|
||||
return Controller._peak_on_able(stub, _lim, [MaintResult(
|
||||
IDLE, False, None, False, [])] * 3)
|
||||
|
||||
|
||||
# x 25 % (cut-out 10 -> floor 20), y 40 % (cut-out 30), z 15 % (held). x and
|
||||
# y each clear their own floor, but 25 % is not above y's 30 %: the pair is
|
||||
# graded min SoC against max floor (SAFETY-32), so nothing is asked.
|
||||
_t3 = three((25.0, 40.0, 15.0), (10, 30, 10))
|
||||
_t3ok = three((35.0, 40.0, 15.0), (10, 30, 10))
|
||||
check("⭐ three batteries, z held: the two others are graded as a pair, the "
|
||||
"EMPTIEST of them (25 %) against the HIGHEST of their floors (30 %), "
|
||||
"so nothing is asked; at 35 % they are asked for the need",
|
||||
_t3.held == ("z",) and _t3.floor_limited and _t3.floor_w == 0.0
|
||||
and _t3ok.held == ("z",) and not _t3ok.floor_limited
|
||||
and _t3ok.floor_w == 2000.0)
|
||||
|
||||
# ============================================================================ #
|
||||
print("\n6. the allocator: a held battery gives last")
|
||||
|
||||
@@ -474,6 +502,13 @@ check("...the row names A and says the others cannot hold the limit alone, "
|
||||
len(_row) == 1 and _row[0]["text"].startswith(
|
||||
"inverter-a battery at its peak reserve. The others cannot hold "
|
||||
"the 3 500 W peak limit alone."))
|
||||
_c65._peak_floor_limited = False
|
||||
_blip = [r for r in _c65.checks() if r.get("id") == "peak_guard"]
|
||||
_c65._peak_floor_limited = True
|
||||
check("...and while the alarm is not latched (a one-cycle miss) the row is "
|
||||
"the ordinary amber guard row, not the red reserve row",
|
||||
len(_blip) == 1 and _blip[0]["warn"] and not _blip[0].get("runs")
|
||||
and "peak reserve" not in _blip[0]["text"])
|
||||
_c65.auto = False
|
||||
_stopped = _c65.status_payload()
|
||||
_c65.auto = True
|
||||
|
||||
Reference in New Issue
Block a user