SAFETY-40: the integrator bound is not a BUG SIGNAL; numbers from the clamp tick
checks / unit (pull_request) Successful in 8m47s
infra-pin / fresh (pull_request) Successful in 1s

The clamp alarm gave `i_clamped` (normal anti-windup) the over-rating BUG
SIGNAL text, printed `Decision.i_w` as "the loop asked for", and printed
`self.target` (after slew/freeze) as "was clamped to".

- clamp.loop_clamp_note(): i_clamped says "the loop's integrator reached its
  N W anti-windup bound" with this tick's command; clamped keeps BUG SIGNAL
  with the requested command and the clamp's own result.
- Decision carries i_bound_w / requested_w / clamped_w, set at the clamp.
- i_clamped still counts and notifies (same id, cadence, SAFETY-41
  escalation) but as a WARNING, amber row, plain title. A bug has its own
  notification budget and its text is not overwritten within an episode.
- test_clamp.py part 9: red on the old code per defect; 5 mutations killed.
- DOCS.md alarm section (and its stale 30 s cadence), DECISIONS/SAFETY-40.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LyR88rXETvwhTgx6yHE39p
This commit is contained in:
glenn schrooyen
2026-09-26 22:37:01 +02:00
co-authored by Claude Opus 5.5
parent 59415ea83f
commit fd14569a71
6 changed files with 340 additions and 54 deletions
@@ -0,0 +1,26 @@
# SAFETY-40: the integrator bound is not a bug
**Decision.** `i_clamped` (the integrator reaching `integrator_max_w`, i.e.
anti-windup) keeps alarming through the same `ClampWatchdog`: it is counted,
notified on the same id and cadence, and escalates on a sustained run
(SAFETY-41). It gets its own text, a `WARNING` and an amber status row. Only
`clamped` (a command over `max_w`) and write-path clamps keep the BUG SIGNAL,
the `ERROR` and the red row.
**Why it still notifies.** On a default install `clamped` is unreachable
(`control.py`, at the integrator bound), so the integrator bound is the only
loop-side signal of a runaway such as the 14 768 W one. Silencing it would
remove that. The card says the same: "the fix is to say the true thing, not to
say less."
**Why a bug has its own budget.** A genuine over-rating must not wait up to
`REASSERT_S` behind anti-windup notices. `ClampWatchdog` tracks a separate
`last_bug_alarm`, and a bug's text is never overwritten by later anti-windup
text within an episode. With only bug engagements, the cadence is the same as
before (`test_clamp.py` part 9).
**Numbers.** They come from the `Decision` of the clamping tick (`i_bound_w`,
`requested_w`, `clamped_w`, `target_w`), not from `i_w` or `self.target`.
**Open for the owner.** Whether scattered one-cycle anti-windup episodes should
notify at all, or only log and count. They notify now, as before.