SAFETY-119 round 2: one settle window per run of hand-backs
checks / unit (pull_request) Failing after 42m32s
infra-pin / fresh (pull_request) Successful in 2s

A hand-back used to restart the window, so a guard and a charge taking
turns hid the loop's integrator bound for good (0 warnings in 2 h; 28
before the fix). A run of hand-backs (the wire away less than a settle
between them) now hides the bound for at most 11 bound cycles and 22 s in
all, then it counts. The quarter-edge kick stays hidden. No command or text
changes. Tests for the two reviewer mutants that only unit checks caught
(a restart counted as a hand-back; span only), DECISIONS gives the delay at
2 s, 5 s and 60 s. test_safety87.py:543 shortened.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Bdqr7PiaHcBnzPituu2yTC
This commit is contained in:
glenn schrooyen
2026-10-20 00:52:24 +02:00
co-authored by Claude
parent b9ff6acea4
commit e76c70ecb0
4 changed files with 269 additions and 44 deletions
@@ -17,21 +17,42 @@ until the plant's settle has passed (`SHORT_CYCLES` cycles and
counts as before. An over-rating (`clamped`, a bug) on a cycle the loop
decided always counts. No command changes, and no text was added or changed.
**One window per run of hand-backs (round 2).** A run is hand-backs with the
wire away less than a settle (12 cycles and 22 s) between them. It ends when
the loop holds the wire, or the wire is away, a whole settle in a row. Each
hand-back restarts the settle, but a run hides the bound for at most 11 bound
cycles and 22 s in all. Round 1 restarted the window at every hand-back, so
a guard and a charge taking turns hid the bound for good (0 warnings in 2 h
where 28 were raised before). Now a bound that stays warns after at most 11
of its cycles, however often the wire changes hands.
**Delay.** The first counted bound cycle, from the hand-back (worst case):
| meter | one hand-back | repeated hand-backs |
|---|---|---|
| 2 s | 22 s | 264 s |
| 5 s (default) | 55 s | 660 s |
| 60 s | 660 s | 7920 s |
One hand-back: 11 cycles, never less than 22 s. Repeated: the worst case is
one bound cycle per turn with the wire away 11 cycles between (22 at 1 s),
so 11 turns. It grows with the meter; it stays under a quarter (900 s) up
to a 5 s meter.
Measured: N=2, 5 s meter, cheap-window charge, no usable forecast, house 50 W
over the 5000 W rating: 14 warnings in 2 h (two in each quarter after the first), 0 with
round 1, 28 without any rule. The cap held.
**Kept.** The loop in charge at its rail warns on the same cycle as before.
A loop still at its rail after a hand-back warns 11 cycles later (at least
22 s): 22 s at a 2 s meter, 55 s at 5 s, 11 min at a 60 s cycle. The start
and a restart after a stop are not hand-backs.
The start and a restart after a stop are not hand-backs. The quarter-edge
kick of `test_safety119.py` part 1 stays hidden (0 warnings in 2 h).
**Re-pinned.** test_safety84 (all six) and test_safety87 (`n1_crossing`,
`n1_above`, `n1_charge_low`): only OUTPUT_CLAMP log rows differ; commands and
claims are the same (`test_safety119.py` part 5).
claims are the same (`test_safety119.py` part 6).
**Open.** Each hand-back restarts the window, so a loop that loses the wire
again within 11 cycles is never counted. Seen in review: N=2 (2 x 2500 W), a
5 s meter, a cheap-window charge with no usable forecast, a house 50-100 W
over the 5000 W rating. The guard and the charge took the wire back at least
every 11 cycles. 0 notifications in 2 h, 28 without this rule. The cap held.
From 200 W over the rating it warns at least once a quarter.
**Not hidden.** A battery that gives less than another owner told it still
alarms through SAFETY-85 rule 2. "Always counts" for an over-rating holds on
cycles the loop decided; on others SAFETY-85 rule 1 already dropped it.
**Not changed.** The kick itself is control behaviour: for a few seconds at
a quarter's edge the loop asks up to its rail. In `test_safety119.py` part 1