SAFETY-85: no false 'not keeping up' while maintenance or the peak guard sets the battery
checks / unit (pull_request) Successful in 25m35s
infra-pin / fresh (pull_request) Successful in 2s

The loop's integrator bound counts only on a cycle the loop decided. A battery
another owner set (maintenance, the peak guard, a safety limit) alarms only
when it gives more than saturation_w less than told for saturation_cycles
cycles. At N>=2 the loop is bounded while maintenance holds a battery, so it
does not wind and control comes back one slew step a cycle. The status row
counts episodes, not cycles. Digests re-pinned where behaviour changed on
purpose; DECISIONS/SAFETY-85-no-false-not-keeping-up.md has the list.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019DBUfvndgHwaV339Fvd2gr
This commit is contained in:
glenn schrooyen
2026-10-01 19:05:27 +02:00
co-authored by Claude Opus 5.5
parent 935878193b
commit 4b81387908
12 changed files with 554 additions and 35 deletions
+20 -6
View File
@@ -108,15 +108,27 @@ A5DDDAD = {
_claims, _undo_c = spy_claims()
_bounds, _undo_b = spy_bounds()
try:
_now = rig.digests()
_now = {k: rig.digest(rig.run(**v)[0])
for k, v in rig.SCENARIOS.items() if k != "n2_maint"}
_now["resolve"] = rig.digest(rig.resolve_rows())
finally:
_undo_b()
# SAFETY-85: n2_maint (maintenance at N=2) is the one run with a bound.
_bounds_m, _undo_b = spy_bounds()
try:
_now["n2_maint"] = rig.digest(rig.run(**rig.SCENARIOS["n2_maint"])[0])
finally:
_undo_c()
_undo_b()
# SAFETY-78 changes n2_maint ON PURPOSE: on a5dddad and 5a4cdc2 its N=2 drain
# was refused on 392 of 540 cycles (site at 0 W); now none is. Printed on the
# SAFETY-78 tree the same way. Every other digest is unchanged.
# SAFETY-85 changes it again ON PURPOSE: while A's drain pin stands the loop
# is bounded by what the batteries can place, so when A drops out at t=1655 B
# follows the house down at once instead of unwinding 920 W too high. Was
# cd4d9cb5b7e29ff4ab4fe65dab5106683d8a599f09323fbdee4018ad9b28fcd1.
SAFETY78_N2_MAINT = \
"cd4d9cb5b7e29ff4ab4fe65dab5106683d8a599f09323fbdee4018ad9b28fcd1"
"1afcd46b1b987868b110d2b0f4a7555848c9aa4728a836d4a2ce2a2107268497"
for k in A5DDDAD:
print(f" {k:7} {'same' if _now[k] == A5DDDAD[k] else 'DIFFERS'} "
f"{_now[k][:16]}")
@@ -129,12 +141,14 @@ check("⭐ at N=1 and N=2, with the option absent and with it false, every "
{k: v for k, v in _now.items() if k != "n2_maint"}
== {k: v for k, v in A5DDDAD.items() if k != "n2_maint"})
check("...and n2_maint is the SAFETY-78 digest: the N=2 drain that a5dddad "
"refused on 392 of 540 cycles now runs (test_safety78.py)",
"refused on 392 of 540 cycles now runs (test_safety78.py), bounded by "
"SAFETY-85",
_now["n2_maint"] == SAFETY78_N2_MAINT != A5DDDAD["n2_maint"])
check("...and no cycle in those runs filed a `mode` claim or handed "
"`compute()` a bound",
check("...and no cycle in those runs filed a `mode` claim, and only "
"n2_maint's (SAFETY-85: maintenance at N=2) handed `compute()` a bound",
_claims and not any(c.source == "mode" for cl in _claims for c in cl)
and _bounds and set(_bounds) == {None})
and _bounds and set(_bounds) == {None}
and any(b is not None for b in _bounds_m))
# ============================================================================ #