SAFETY-03 review fixes: both clamp stages always run, and two checks that could not fail
1. BEHAVIOURAL, and a regression this PR introduced. clamp_to_rating() returned early from the max_w stage, so the device bound was applied only when the rating bound had NOT fired - the bigger the overshoot, the less the guard did. At max_w 5000 against a +-4000 device, +9999 emitted +5000 W, outside the device range, while +4500 was correctly held to +4000. It undid the pre-PR guarantee "never send a value the device will reject outright" and undercut this module's own case for max_w-not-min(), which rests on that stage catching a validation that did not run. Both stages now always run; the result names both bounds when both bite (max_w+device). Asserted over a 492-point sweep, because the defect was invisible to any single-value check sitting inside one stage. 2. The hysteresis check issued range(CLEAR_CYCLES - 1) clean cycles, which at CLEAR_CYCLES = 1 issues zero, passes, and prints "0 clean cycles do NOT clear it" - green for the chatter setting the constant exists to prevent. Now: ONE clean cycle must not clear the alarm, a literal that does not move with the constant, and the de-assert count is measured by looping until it clears. 3. The re-assert check stimulated at REASSERT_S + 9, green for any value including 3600 - the hour of silence the module promises not to produce. Now stated in wall-clock terms: a loop pinned across the 900 s capacity-tariff quarter must speak at least 5 times and on no more than 20% of cycles. Pinned in both directions. Red confirmed for each: the restored early return (3 checks), CLEAR_CYCLES = 1 (1), REASSERT_S = 3600 (1) and REASSERT_S = 0 (3). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015VXjA3LWccN1rJsk3okyPC
This commit is contained in:
co-authored by
Claude Opus 5
parent
c040aac4b8
commit
4e66583a52
@@ -55,6 +55,34 @@ out-of-range value. Measured: because every comparison against `nan` is False, a
|
||||
naive `max(-limit, min(limit, nan))` keeps its *first* argument and turns a `nan`
|
||||
into a command **at the full rating**, then reports it as a clamp engagement.
|
||||
|
||||
**Review fixes (PR #12).** Three, and the first was a behavioural regression
|
||||
this PR introduced: `clamp_to_rating()` **returned early** from the `max_w`
|
||||
stage, so the device bound - the "applied second, defence in depth" half - was
|
||||
reached only when the rating bound had NOT fired. The bigger the overshoot, the
|
||||
less the guard did. At `max_w` 5000 against a +-4000 device, +9999 emitted
|
||||
**+5000 W, outside the device range**, while a mere +4500 was correctly held to
|
||||
+4000. That undid the pre-PR guarantee "never send a value the device will
|
||||
reject outright" and undercut this module's own case for clamping to `max_w`
|
||||
rather than a runtime `min()`, which rests entirely on that stage catching a
|
||||
validation that did not run. Both stages now always run, the result names both
|
||||
bounds when both bite (`max_w+device`), and the property is asserted over a
|
||||
492-point sweep rather than on witness values - the defect was invisible to any
|
||||
single-value check that happened to sit inside one stage. No reachable
|
||||
production path was found for it (two validation gates and the arbiter bound sit
|
||||
in front), but latent is not harmless.
|
||||
|
||||
Two harness defects, both of the kind where a check cannot fail for the value it
|
||||
forbids: the hysteresis check issued `range(CLEAR_CYCLES - 1)` clean cycles,
|
||||
which at `CLEAR_CYCLES = 1` issues **zero** and passes while printing
|
||||
"0 clean cycles do NOT clear it" - green for the chatter setting the constant
|
||||
exists to prevent. And the re-assert check stimulated at `REASSERT_S + 9`, green
|
||||
for **any** value including 3600, i.e. the hour of silence the module promises
|
||||
not to produce. Both are now written in wall-clock terms that do not move when
|
||||
the constant does: **one** clean cycle must not clear the alarm, and a loop
|
||||
pinned across the 900 s capacity-tariff quarter must speak at least 5 times and
|
||||
on no more than 20 % of cycles. `CLEAR_CYCLES = 1`, `REASSERT_S = 3600` and
|
||||
`REASSERT_S = 0` each go red.
|
||||
|
||||
**`test_clamp.py`** (new, assert-based, no framework) covers the clamp
|
||||
arithmetic and its boundaries - exactly +-`max_w` is *not* an engagement - the
|
||||
alarm's edge/re-assert/hysteresis cadence, the 10.3 runaway replayed through the
|
||||
|
||||
Reference in New Issue
Block a user