Follow-up from #3790. The time-denominated dwell gate landed in #3790 fixes the 2x streak-acceleration bug in eval_counter_streak (the recovery-stalled alarm). The identical tick-vs-time root cause exists in eval_counter_ratio (scripts/lib/eval-alarms.py): its {name}_streak advances once per breaching tick, so a duplicate/split interval advances the ratio streak at 2x wall-clock speed and reaches streak >= streak_threshold early — same defect, same fix.
Affected alarms (kind = counter-ratio): scp-accept-rate-low, apply-failure-ratio, pending-too-old-ratio.
Fix: port the #3790 pattern to eval_counter_ratio — add a per-alarm {name}_first_breach_ts to the ratio_snapshot, set it on the 0→1 streak open, preserve it while advancing, clear it on every reset branch (low-volume, den==0, non-breaching, gap-stale, too-fresh, counter/PID reset), and gate the fire on now - first_breach_ts >= streak_threshold * expected_interval_seconds in addition to the count. Reuse the expected_interval_seconds config field (default 1200s) introduced in #3790, and plumb now into eval_counter_ratio (already threaded into eval_counter_streak).
Deferred from #3790 to keep that PR atomic on the counter-streak family (the filed incident). See #3757 comment 5105990247 for the root-cause writeup.
Follow-up from #3790. The time-denominated dwell gate landed in #3790 fixes the 2x streak-acceleration bug in
eval_counter_streak(therecovery-stalledalarm). The identical tick-vs-time root cause exists ineval_counter_ratio(scripts/lib/eval-alarms.py): its{name}_streakadvances once per breaching tick, so a duplicate/split interval advances the ratio streak at 2x wall-clock speed and reachesstreak >= streak_thresholdearly — same defect, same fix.Affected alarms (kind = counter-ratio):
scp-accept-rate-low,apply-failure-ratio,pending-too-old-ratio.Fix: port the #3790 pattern to
eval_counter_ratio— add a per-alarm{name}_first_breach_tsto theratio_snapshot, set it on the 0→1 streak open, preserve it while advancing, clear it on every reset branch (low-volume, den==0, non-breaching, gap-stale, too-fresh, counter/PID reset), and gate the fire onnow - first_breach_ts >= streak_threshold * expected_interval_secondsin addition to the count. Reuse theexpected_interval_secondsconfig field (default 1200s) introduced in #3790, and plumbnowintoeval_counter_ratio(already threaded intoeval_counter_streak).Deferred from #3790 to keep that PR atomic on the counter-streak family (the filed incident). See #3757 comment 5105990247 for the root-cause writeup.