SAFETY-43 round 2: the cut-out "positive control" was vacuous, and the record
checks / unit (pull_request) Successful in 6m29s

stated the wrong mechanism

⚠️ REVIEW FINDING, BLOCKING, AND IT IS THE EVIDENCE THAT WAS WRONG RATHER THAN
THE BEHAVIOUR. Part 8b's cut-out arm claimed an "identical power trace" to the
wedged arm and that "what separates them is `account()`". EVERY PART OF THAT WAS
FALSE, and the check survived deleting the gate it named. Measured:

    cut-out arm, account() live        named 0/200, distinct B shares: [0]
    cut-out arm, account() FORCED None named 0/200, distinct B shares: [0]
    wedged  arm, account() live        named 200/200, shares 77/125/133/154 W

At its own cut-out the pack's usable energy ABOVE that cut-out is zero, so
`weights()` answers 0.0, `_fill()`'s preferred pass gives it nothing, the healthy
sibling covers the load alone so the last-resort pass never runs, and its share
is 0 W ON ALL 200 CYCLES. A unit with a 0 W share is not in `asked`, so
`len(asked) < 2` returns {} BEFORE EITHER ARM IS EVALUATED and `account()` is
never consulted. Nothing was identical, "100 % of the ask" was 0/0, and the arm
never reached the comparison it was written to grade.

WHAT REPLACES IT IS A BETTER FACT, NOT A WEAKER ONE: at its own cut-out OPT-09
STOPS ASKING THE PACK AT ALL, so SAFETY-43's relative arm is never reached. That
is a SECOND, INDEPENDENT protection sitting UPSTREAM of `account()` - the
allocator declines to ask before the detector has to decline to alarm. So the arm
is kept and re-pointed, not deleted:

  * the 0 W share is now ASSERTED (`_sat_last[1][Bee] == 0`), not inferred from
    the silence - a control that cannot say WHY it is quiet cannot fail;
  * the arm runs a SECOND time with `account()` DELETED (`no_account=True`) and
    must still be silent, which is what proves which mechanism does the work;
  * the account gate's claim is re-pointed at test_delivery.py part 1, where it
    is graded on a REAL positive 125 W share.

⚠️ AND IT WAS INVISIBLE FOR ONE REASON WORTH KEEPING: that was the ONLY line in
part 8b that did not print its shares. `inverter-b+0` would have been on screen
from the first run. Every line in the part prints them now. The derated control's
vacuity guard also now asserts ITS OWN unit is asked, rather than re-asserting a
different run's 200/200 - which is exactly why the old guard could not see this.

ALSO, from the same review:

  N1  app/delivery.py said 0.05 was "twelve times more sensitive" than 0.85.
      0.85/0.05 = 17 and no reading of the pair gives 12. An unsupported number
      in shipped source; replaced with what was actually measured.
  N2  M14's recorded count was 4 named checks in test_delivery.py and it is 5 -
      part 1's pre-existing "a shortfall AT the threshold is not one" also goes
      red. It under-claimed, so it never inflated the evidence, but a recorded
      figure that does not reproduce is still one.
  N3  test_delivery.py part 1 printed the alarm sentence with a bare
      `delinquent(...)["b"]`, which raises KeyError under M12/M13 and ABORTED
      THE FILE AT PART 1, leaving parts 2-8 ungraded. The direction is safe (an
      abort is a red; MUTATION.md's defect fabricates only greens) but the
      recorded coverage read smaller than the truth. It is a `.get` now, and
      M12/M13's counts are re-run: 7 and 7 named checks, not 5 and 6.

CFG-07: the assessment is unchanged (worse by one control cycle, 35.0 -> 40.0 s,
deliberately not fixed here) and now says WHEN it has to close - BEFORE INVERTER
B IS COMMISSIONED (~2026-09-12), because the band is unreachable at N=1 and
commissioning B is when it first becomes reachable - and that CFG-07 HAS NO
DECISION DOCUMENT IN THIS TREE, so whoever takes it writes one first.

No behaviour change. `delinquent()`'s two arms, the fraction, `min_w` and the
N=1 path are all untouched by this commit. 27/27 suites green.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BwRLw7eKYj2byVdwB6kbjU
This commit is contained in:
Claude Opus 5
2026-09-08 04:15:07 +02:00
parent 2271e147c6
commit b21e229adf
5 changed files with 216 additions and 68 deletions
+90 -32
View File
@@ -662,7 +662,7 @@ ABSOLUTE_ONLY = None # `share_frac=None` - SAFETY-42's rule
def wedged_loop(load_w, weighted, cycles=200, share_frac=DELIVERY_SHARE_FRACTION,
b_delivers=0.0, b_soc=WEDGED_B_SOC):
b_delivers=0.0, b_soc=WEDGED_B_SOC, no_account=False):
"""The closed loop with unit B WEDGED at a healthy SoC. Real everything.
Real `compute()`, real `plan()`, real `packshare.weights()`, real
@@ -679,6 +679,12 @@ def wedged_loop(load_w, weighted, cycles=200, share_frac=DELIVERY_SHARE_FRACTION
cycles under SAFETY-42's rule, which is how the red and the green below come
out of ONE run over ONE tree instead of out of two commits.
`no_account=True` DELETES the account gate - every unit is handed
`accounted=None` - and it exists to answer "would this arm still be silent
if `account()` did nothing at all?". A control that answers the same way
with the gate removed is not grading the gate, and the cut-out arm below is
exactly that case. See it there; it is the point of that block.
Returns `(cycles on which delinquent() named somebody, last cycle)`.
"""
packs = [PE(A, WEDGED_A_SOC, A_KWH), PE(Bee, b_soc, B_KWH)]
@@ -699,6 +705,7 @@ def wedged_loop(load_w, weighted, cycles=200, share_frac=DELIVERY_SHARE_FRACTION
grid = load_w - sum(power)
who = delinquent(
[UnitDelivery(p.name, r.shares[p.name], m,
None if no_account else
account(live=True, parked=False, soc=p.soc,
bms_floor=p.bms_floor))
for p, m in zip(packs, power)], MIN_W, share_frac)
@@ -825,18 +832,56 @@ print(" THE POSITIVE CONTROL: what the relative arm must STILL NOT say")
# HA". So: the same fixture, the same weights, the same tiny 125 W share, with
# the ONE fact that makes the shortfall legitimate changed.
# ⚠️ AND THE FIRST DRAFT OF THIS BLOCK WAS VACUOUS, WHICH IS WHY IT NOW PRINTS
# ITS SHARES. It ran the cut-out arm, saw 0/200, and concluded "`account()`
# explains it" and "identical trace to the wedged arm". BOTH WERE FALSE. At its
# own cut-out the pack's usable energy ABOVE that cut-out is zero, so
# `weights()` gives it 0.0, `_fill()`'s preferred pass gives it nothing, the
# healthy sibling covers the load alone so the LAST-RESORT pass never runs, and
# its share is 0 W on ALL 200 CYCLES. A unit with a 0 W share is not in `asked`,
# so `len(asked) < 2` returns {} BEFORE EITHER ARM IS EVALUATED and `account()`
# is never consulted - measured below by deleting the gate and getting the same
# answer. The wedged arm's share is the 125 W printed on its own line above;
# this one's is 0, printed and asserted below. Nothing was identical and nothing
# reached the comparison.
#
# ⚠️ THE FACT THAT REPLACES IT IS BETTER, NOT WEAKER: at its own cut-out OPT-09
# STOPS ASKING THE PACK AT ALL, so SAFETY-43's relative arm is never reached.
# That is a SECOND, INDEPENDENT protection sitting UPSTREAM of `account()` - the
# allocator declines to ask before the detector has to decline to alarm. The
# account gate itself is graded where it is REAL, on a positive 125 W share, in
# `test_delivery.py` part 1 ("THE ACCOUNT GATE IS UNTOUCHED BY IT").
#
# ⚠️ AND IT WAS INVISIBLE FOR ONE REASON: this was the only line in part 8b that
# did not print its shares. `inverter-b+0` would have been on screen from the
# first run. Every line in this part prints them now.
_sat_named, _sat_last = wedged_loop(1500.0, True, b_soc=FLOOR)
_sat_nogate, _sat_nogate_last = wedged_loop(1500.0, True, b_soc=FLOOR,
no_account=True)
print(f" load 1500 W, B AT ITS OWN {FLOOR:.0f} % CUT-OUT, OPT-09 weights, "
f"frac {DELIVERY_SHARE_FRACTION:.2f}: named on {_sat_named:3d}/200 "
f"cycles, named="
+ (str(sorted(_sat_last[3])) if _sat_last[3] else "(nobody)"))
check("A PACK AT ITS OWN CUT-OUT DELIVERING NOTHING OF A SMALL SHARE IS STILL "
"NOT DELINQUENT. Identical trace to the wedged arm - 0 W against a "
"positive share, 100 % of the ask missing, the relative arm's exact "
"shape - and `account()` explains it, so nobody is named on any of the "
"200 cycles. The account gate is what keeps normal saturation out and "
"SAFETY-43 did not go near it",
_sat_named == 0 and _sat_last[3] == {})
f"cycles, shares "
+ ", ".join(f"{u}{v:+d}" for u, v in sorted(_sat_last[1].items()))
+ ", named="
+ (str(sorted(_sat_last[3])) if _sat_last[3] else "(nobody)")
+ f"; with account() DELETED: {_sat_nogate:3d}/200")
check("A PACK AT ITS OWN CUT-OUT IS NEVER ASKED, SO THE RELATIVE ARM IS NEVER "
"REACHED. Its usable energy above its own cut-out is zero, `weights()` "
"answers 0.0 for it, the healthy sibling covers the load so `_fill()`'s "
"last-resort pass never runs, and its share is 0 W on every one of the "
"200 cycles - so it is not in `asked` and `len(asked) < 2` returns {} "
"before either arm is compared. ⚠️ THE SHARE IS ASSERTED, not inferred "
"from the silence: a control that cannot say WHY it is quiet is a control "
"that cannot fail",
_sat_named == 0 and _sat_last[3] == {} and _sat_last[1][Bee] == 0)
check("⚠️ AND THAT IS A SECOND, INDEPENDENT PROTECTION - NOT THE ACCOUNT GATE, "
"WHICH THIS ARM DOES NOT TOUCH. Delete `account()` entirely, hand every "
"unit `accounted=None`, and this arm is STILL silent on all 200 cycles, "
"because the protection is UPSTREAM of it: the allocator declines to ask "
"before the detector has to decline to alarm. ⚠️ SO DO NOT READ THIS "
"BLOCK AS EVIDENCE ABOUT `account()` - it is evidence about the ask. The "
"account gate on a POSITIVE share is graded in test_delivery.py part 1",
_sat_nogate == 0 and _sat_nogate_last[3] == {})
_derate_named, _derate_last = wedged_loop(1500.0, True, b_delivers=0.5)
print(f" load 1500 W, B delivering HALF its share, OPT-09 weights, frac "
@@ -853,11 +898,15 @@ check("...and ORDINARY TRACKING ON A SMALL SHARE IS NOT DELINQUENCY EITHER. "
"whole module is written against",
_derate_named == 0 and _derate_last[3] == {})
check("...and the two controls are not vacuous: the SAME loop with the SAME "
"weights and a WEDGED B names it on all 200, so what separates them is "
"the pack's own cut-out and the size of the shortfall, not something "
"about the fixture that stopped the check reaching the comparison",
_wedge[(1500.0, True, DELIVERY_SHARE_FRACTION)][0] == 200)
check("...and the DERATED control is not vacuous, which is asserted on the ONE "
"thing that could make it so: unlike the cut-out arm above, its unit IS "
"asked - a positive share on every cycle - so it really does reach both "
"arms and is turned away by the size of its shortfall. ⚠️ THIS GUARD USED "
"TO RE-ASSERT THE WEDGED ARM's 200/200 INSTEAD, WHICH IS WHY IT DID NOT "
"CATCH THE CUT-OUT ARM BEING VACUOUS: a check on a DIFFERENT run cannot "
"say anything about whether THIS one reached the comparison",
_derate_last[1][Bee] > 0
and _wedge[(1500.0, True, DELIVERY_SHARE_FRACTION)][0] == 200)
print()
print(" N=1 IS BYTE-IDENTICAL, AND THAT IS ASSERTED RATHER THAN ARGUED")
@@ -1186,24 +1235,27 @@ MUT = [
# --- SAFETY-43's three, and they pin the new arm from BOTH sides --------
("M12 delinquent(): drop the relative arm, keep `gap > min_w` <- THE "
"CARD ITSELF REVERTED",
"test_delivery 1 (one named check) and test_packshare 8b (FOUR): the "
"GREEN check goes back to 0/200 under OPT-09's weights, the equal arm "
"goes back to 199, the 6000 W arm back to 195/199, and the vacuity guard "
"on the positive controls falls with them. test_alloc and test_control "
"stay green, which is the point - nothing outside this check moves"),
"SEVEN named checks: test_delivery 1 (THREE - the 0-of-125 W naming, the "
"proportion in the sentence, and the strict relative bound) and "
"test_packshare 8b (FOUR - the GREEN check goes back to 0/200 under "
"OPT-09's weights, the equal arm's 200 back to 199, the 6000 W arm back "
"to 195/199, and the derated control's vacuity guard falls with them). "
"test_alloc and test_control stay green, which is the point - nothing "
"outside this check moves"),
("M13 DELIVERY_SHARE_FRACTION 0.85 -> 1.0 <- THE TOO-LOOSE DIRECTION",
"test_delivery 1 and test_packshare 8b, five named checks including the "
"PRECONDITION that the fraction is strictly below 1.0. At 1.0 the arm can "
"only fire on a unit IMPORTING while asked to discharge, so the card's "
"own case - 0 of 125 W, gap over share exactly 1.0 - is missed and the "
"whole card is a no-op"),
"SEVEN named checks: test_delivery 1 (TWO) and test_packshare 8b (FIVE), "
"including the PRECONDITION that the fraction is strictly below 1.0. At "
"1.0 the arm can only fire on a unit IMPORTING while asked to discharge, "
"so the card's own case - 0 of 125 W, gap over share exactly 1.0 - is "
"missed and the whole card is a no-op"),
("M14 DELIVERY_SHARE_FRACTION 0.85 -> 0.05 <- THE TOO-SENSITIVE ONE",
"test_delivery 1 and 4b (four named checks) and test_packshare 8b's "
"DERATED positive control: a unit delivering half its share starts being "
"named, and part 4b's `saturation_w` pin goes vacuous because the "
"relative arm names B at every threshold. ⚠️ THIS IS THE ROW THAT MATTERS "
"- the fraction is pinned from BOTH directions, so it is a derived number "
"with checks around it rather than a literal nobody can move"),
"SIX named checks: test_delivery 1 (TWO - the strict bound, and 'IT TOOK "
"NOTHING AWAY' stops being true) and 4b (THREE - the `saturation_w` pin "
"goes VACUOUS, because the relative arm names B at every threshold), plus "
"test_packshare 8b's DERATED positive control, where a unit delivering "
"half its share starts being named. ⚠️ THIS IS THE ROW THAT MATTERS - the "
"fraction is pinned from BOTH directions, so it is a derived number with "
"checks around it rather than a literal nobody can move"),
]
for _n, _w in MUT:
print(f" caught {_n}")
@@ -1216,7 +1268,13 @@ check("every mutation in the table above was RUN and every one of them is "
"why the call site is pinned statically as well. ⚠️ M12-M14 ARE "
"SAFETY-43's and were run the same way, on trees copied fresh and "
"mounted :ro so no `.pyc` could go stale - MUTATION.md's defect. All "
"three are REDS, which is the direction that defect cannot fabricate",
"three are REDS, which is the direction that defect cannot fabricate. "
"⚠️ AND THEIR COUNTS WERE RE-RUN IN REVIEW AND TWO OF THEM MOVED: "
"test_delivery.py part 1 used to subscript `delinquent(...)['b']` for a "
"print, which raised KeyError under M12 and M13 and ABORTED THE FILE at "
"part 1, leaving parts 2-8 ungraded and the recorded coverage smaller "
"than the truth. It is a `.get` now. M14's count was simply wrong: five "
"named checks in test_delivery, not four",
len(MUT) == 14)
print()