Add an optional Critical tier for between/outside range alert predicates (#3372) - #3380
Merged
Merged
Conversation
…tes (#3372) Range custom-alert ops (#3351/#3378) were Warning-only. This adds an OPTIONAL second, more-severe band via two nullable predicate fields criticalLowerBound / criticalUpperBound; absent, a range rule is Warning-only exactly as before. "More severe" means further from healthy, which flips the nesting per op: - outside breaches further OUT, so its crit band is WIDER and CONTAINS the warn band (criticalLowerBound <= lowerBound < upperBound <= criticalUpperBound). - between breaches DEEPER in, so its crit band is NARROWER and is CONTAINED BY the warn band (lowerBound <= criticalLowerBound <= criticalUpperBound <= upperBound). Severity routes through the single RangeBreaches band authority evaluated at BOTH bands: a breaching range op is Critical iff the crit band exists and the value also breaches it (the crit triple), else Warning. Because the crit firing region is a subset of the warn firing region under the enforced nesting, IsBreaching (the warn band) stays the "breaches at all" envelope and the #3378 COUNT-on-zero guard on the warn band is a complete 0-guard for both bands (no parallel crit-band math). Validation: crit bounds are all-or-nothing, numeric, and op-specifically nested; a scalar op still rejects any range/crit bounds and a range op still rejects scalar thresholds. Delivered-alert render and the rule-card chip convey both bands (outside 10 - 100 (crit outside 5 - 200) / between 10 - 100 (crit 40 - 60)); the numeric threshold twin stays null for range. The editor offers two optional Critical bound inputs when a range op is selected, with nesting help text, mapped to/from the new fields; the backend /api/alerts/validate stays the authority. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Y5xoN5PzhzYfHHrfutyeEe
|
Reviewed. This is a clean, well-scoped addition — no issues found.
No blocking findings. |
This was referenced Sep 12, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Adds an optional Critical tier for the
between/outsiderange custom-alert predicates (#3372, part of #3285). Range ops (#3351/#3378) were Warning-only; they can now carry a second, more-severe band via two new nullable predicate fieldscriticalLowerBound/criticalUpperBound. Absent → Warning-only (today's behavior, unchanged). Scalar ops (gt/ge/lt/le) are untouched.Predicate JSON shape (two-tier range rule)
The existing
lowerBound/upperBoundare the Warning band; the crit pair is the Critical band.How severity routes through
RangeBreaches"More severe" means further from healthy, so the nesting flips by op. The shared private
RangeBreaches(op, lower?, upper?, value)stays the single band authority and is evaluated at both bands — the crit band is just another(op, lower, upper)triple:IsBreaching(value)tests the warn band → "breaches at all".SeverityFor(value)for a range op → Critical iff the crit band exists ANDRangeBreaches(Op, criticalLowerBound, criticalUpperBound, value), else Warning.Because the validator enforces the nesting, the crit firing region is always a subset of the warn firing region, so the warn band is the outer envelope and
SeverityForonly asks the incremental "also in crit?" question. No parallel band math. The #3341 open-incident severity-change path already callsSeverityFor, so a range rule now escalates Warning→Critical (and de-escalates) automatically.Op-specific nesting validation
outside:criticalLowerBound <= lowerBound < upperBound <= criticalUpperBound(WIDER). A too-narrow crit band is rejected.between:lowerBound <= criticalLowerBound <= criticalUpperBound <= upperBound(NARROWER, and its own bounds ordered). A too-wide or inverted crit band is rejected.warnThreshold/criticalThreshold.#3378 COUNT-on-zero guard interaction
The guard tests the warn band (
RangeBreaches(op, lower, upper, 0)), which is a complete 0-guard for both bands: the crit band was just validated to nest inside the warn firing region, so if the warn band doesn't fire on 0 the crit band can't either — and any crit band that would fire on 0 could only do so alongside a warn band that also fires on 0, which is already rejected. No separate crit-band 0-check is needed. The guard still fires with a crit band present (regression covered).Render + editor
FiredThreshold) + rule-card chip convey both bands:outside 10 - 100 (crit outside 5 - 200)/between 10 - 100 (crit 40 - 60)(card chip uses the existing en-dash style). Numeric threshold twin staysnullfor range, consistent with Add between/outside range predicate operators for custom alert rules (#3351) #3371.wwwroot/js/alert-editor.js): two optionalCritical lower bound/Critical upper boundinputs appear for a range op, with help text stating the nesting; mapped to/from the new fields indefinitionToModel/modelToDefinition, with an all-or-nothing + op-nesting client-side save-blocker. Backend/api/alerts/validatestays the authority.validate_custom_alert_ruledescription updated to document the optional crit band.Tests
Darling.Testsbuild clean;CustomAlertRuleDefinitionTests(79) and the pure custom-alert classes (evaluate-now, severity-escalation, display, MCP tools, templates, teardown, health, tag-scope, cap — 70) all pass. New coverage: two-tier severity for both ops (low + high side for outside; warn-not-crit vs crit for between), op-specific nesting rejection (too-narrow outside / too-wide + inverted between), all-or-nothing + non-numeric crit bounds, both-bandFiredThresholdrender, and the COUNT-on-zero guard still firing with a crit band present. Warning-only range regression guards (#3371/#3378) unchanged.Service builds
0 Warning(s) / 0 Error(s).Deferred
Nothing.
🤖 Generated with Claude Code
https://claude.ai/code/session_01Y5xoN5PzhzYfHHrfutyeEe