Alarm Severity mixes two directions: MxGateway and computed rows carry priority (lower = more urgent) while OPC UA rows carry Part 9 severity (higher = worse) #129

Closed
opened 2026-11-12 12:05:00 -05:00 by dohertj2 · 2 comments
Owner

Found while fixing lmxopcua#887 (Galaxy alarm Severity direction). Not a regression from that fix; it makes the OPC UA path (first row below) correct and so exposes the disagreement.

What. AlarmConditionState.Severity is documented as "the unified 0–1000 severity", but the three sources fill it with two opposite conventions:

Source What lands in Severity Direction
Native, OPC UA adapter (OpcUaAlarmMapper.NormalizeSeverity) OPC UA Part 9 Severity, clamped 0–1000 higher = more severe
Native, MxGateway adapter (MxGatewayAlarmMapper.NormalizeSeverity) the gateway's severity, which mxaccessgw fills from the AVEVA alarm priority (AlarmDispatcher: Severity = record.Priority), clamped 0–1000 lower = more urgent (AVEVA: 1 most urgent, 999 least)
Computed (AlarmConditionStateFactory.ForComputed(state, PriorityLevel)) TemplateAlarm.PriorityLevel ("lower values indicate higher priority") lower = more urgent

The consumers all read it as higher = worse:

  • NativeAlarmActor.ErrorSeverityThreshold = 700: an active native alarm with Severity >= 700 logs as Error, else Warning. On the MxGateway path an AVEVA priority-1 (Critical) alarm logs as Warning, and priority 700–999 (AVEVA Medium/Low) as Error.
  • AlarmSummaryService "Worst Severity" = the max Severity; AlarmSummary.razor severity threshold filter is Severity >= min and sorts by Severity. For MxGateway and computed rows that picks the least urgent alarm.

The MES 900–999 band (native alarms). Per docs/plans/2026-06-30-mes-alarm-status-api.md §6.1 the band is configured in the Galaxy alarm priority and tested on Severity. So:

  • It works on the MxGateway path only because that path passes the raw priority through.
  • It cannot work on the OPC UA path (Galaxy via OtOpcUa): OtOpcUa bands Galaxy priority into 250/500/750/1000 (before and after lmxopcua#887), so no Galaxy alarm reaches 900–999 there.
  • In AVEVA's default ranges (1–250 Critical, 251–500 High, 501–750 Medium, 751–999 Low), 900–999 is the Low band: MES-relevant alarms configured into it show AVEVA operators the lowest severity.
  • Inverting the MxGateway path to OPC UA direction would move those alarms to Severity 2–101 and break the band. So the band and the direction have to be decided together.

What lmxopcua#887 changed on the OPC UA path (Galaxy via OtOpcUa). AVEVA priority 1–250 → 1000, 251–500 → 750, 501–750 → 500, 751–999 → 250 (was 250/500/750/1000 in the other order, and live used 200/500/700/900). ScadaBridge code has nothing that depends on the old inverted values. No deployed ScadaBridge reads Galaxy alarms through OtOpcUa today: vd03 ScadaBridge has no OPC UA link to OtOpcUa, and vd03 OtOpcUa has no Galaxy driver.

Owner decision needed.

  1. Pick one direction for AlarmConditionState.Severity. Recommended: OPC UA's, higher = more severe, since the consumers and the OPC UA path already use it. Map MxGateway priority with the same AVEVA bands OtOpcUa uses (GalaxyAlarmPriority) or a linear 1001 - p. Map computed PriorityLevel the same way, or carry it separately.
  2. Re-home the native MES flag: either a raw-priority field next to Severity (keep the 900–999 band on the priority), or move native sources to the binding-level IsMesReportable (#59) and retire the band.
  3. Update ErrorSeverityThreshold, Alarm Summary worst/filter/sort, and the MES MinSeverity/MaxSeverity filter semantics to match.
Found while fixing lmxopcua#887 (Galaxy alarm Severity direction). Not a regression from that fix; it makes the OPC UA path (first row below) correct and so exposes the disagreement. **What.** `AlarmConditionState.Severity` is documented as "the unified 0–1000 severity", but the three sources fill it with two opposite conventions: | Source | What lands in `Severity` | Direction | |---|---|---| | Native, OPC UA adapter (`OpcUaAlarmMapper.NormalizeSeverity`) | OPC UA Part 9 Severity, clamped 0–1000 | **higher = more severe** | | Native, MxGateway adapter (`MxGatewayAlarmMapper.NormalizeSeverity`) | the gateway's `severity`, which mxaccessgw fills from the AVEVA alarm **priority** (`AlarmDispatcher`: `Severity = record.Priority`), clamped 0–1000 | **lower = more urgent** (AVEVA: 1 most urgent, 999 least) | | Computed (`AlarmConditionStateFactory.ForComputed(state, PriorityLevel)`) | `TemplateAlarm.PriorityLevel` ("lower values indicate higher priority") | **lower = more urgent** | The consumers all read it as higher = worse: - `NativeAlarmActor.ErrorSeverityThreshold = 700`: an active native alarm with `Severity >= 700` logs as `Error`, else `Warning`. On the MxGateway path an AVEVA priority-1 (Critical) alarm logs as Warning, and priority 700–999 (AVEVA Medium/Low) as Error. - `AlarmSummaryService` "Worst Severity" = the max `Severity`; `AlarmSummary.razor` severity threshold filter is `Severity >= min` and sorts by `Severity`. For MxGateway and computed rows that picks the *least* urgent alarm. **The MES 900–999 band (native alarms).** Per `docs/plans/2026-06-30-mes-alarm-status-api.md` §6.1 the band is configured in the **Galaxy alarm priority** and tested on `Severity`. So: - It works on the MxGateway path only because that path passes the raw priority through. - It cannot work on the OPC UA path (Galaxy via OtOpcUa): OtOpcUa bands Galaxy priority into 250/500/750/1000 (before and after lmxopcua#887), so no Galaxy alarm reaches 900–999 there. - In AVEVA's default ranges (1–250 Critical, 251–500 High, 501–750 Medium, 751–999 Low), 900–999 is the **Low** band: MES-relevant alarms configured into it show AVEVA operators the lowest severity. - Inverting the MxGateway path to OPC UA direction would move those alarms to Severity 2–101 and break the band. So the band and the direction have to be decided together. **What lmxopcua#887 changed on the OPC UA path (Galaxy via OtOpcUa).** AVEVA priority 1–250 → 1000, 251–500 → 750, 501–750 → 500, 751–999 → 250 (was 250/500/750/1000 in the other order, and live used 200/500/700/900). ScadaBridge code has nothing that depends on the old inverted values. No deployed ScadaBridge reads Galaxy alarms through OtOpcUa today: vd03 ScadaBridge has no OPC UA link to OtOpcUa, and vd03 OtOpcUa has no Galaxy driver. **Owner decision needed.** 1. Pick one direction for `AlarmConditionState.Severity`. Recommended: OPC UA's, higher = more severe, since the consumers and the OPC UA path already use it. Map MxGateway priority with the same AVEVA bands OtOpcUa uses (`GalaxyAlarmPriority`) or a linear `1001 - p`. Map computed `PriorityLevel` the same way, or carry it separately. 2. Re-home the native MES flag: either a raw-priority field next to `Severity` (keep the 900–999 band on the priority), or move native sources to the binding-level `IsMesReportable` (#59) and retire the band. 3. Update `ErrorSeverityThreshold`, Alarm Summary worst/filter/sort, and the MES `MinSeverity`/`MaxSeverity` filter semantics to match.
dohertj2 added the cross-repopriority:p2type:bug labels 2026-11-12 12:05:12 -05:00
Author
Owner

Consumer impact: alarm Severity now runs one direction (higher = more urgent)

This is a visible ordering flip for anyone who reads Severity on an MxAccess Gateway native alarm or a computed alarm. OPC UA rows do not change.

What a reader sees after this build

Source Severity before Severity now Raw value now lives on
OPC UA native (incl. Galaxy via OtOpcUa) Part 9 Severity, clamped 0–1000 unchanged Priority (= Severity; OPC UA has no separate priority)
MxAccess Gateway native AVEVA priority, clamped 0–1000 (1 = most urgent) AVEVA band: 1–250 → 1000, 251–500 → 750, 501–750 → 500, 751–999 → 250. Out-of-range values clamp: 0 or less reads 1000, above 999 reads 250 Priority (clamped 0–1000, i.e. the old Severity)
Computed (PriorityLevel) PriorityLevel verbatim same band as above Priority (PriorityLevel verbatim)

The table is AlarmPriorityBands. It matches OtOpcUa GalaxyAlarmPriority (lmxopcua#887, b61f3e05) value for value, edge rows and clamps included, so a Galaxy alarm now has the same Severity whether it arrives through OtOpcUa or straight from mxaccessgw. OtOpcUa's history-only "not an alarm" row (AVEVA severity 0 → 1) has no counterpart here. The live gateway feed carries only alarms and only their priority, and a computed alarm is always an alarm. Priority 0 therefore clamps to Critical, as OtOpcUa's Map(0) does.

Who sees the flip

  • Central UI Alarm Summary. "Worst Severity" (max), the minimum-severity filter (>=) and the severity sort (descending by default) now put AVEVA priority 1 at the top. Before, they put priority 999 at the top. The numbers in the Sev column change for MxAccess and computed rows.
  • Debug View / alarm badges (sev N). These show the banded number.
  • Site event log alarm category.
    • Native (NativeAlarmActor): the threshold is unchanged (Severity >= 700 → Error). An MxAccess priority 1–500 raise now logs Error and 501–999 logs Warning. Before it was the other way round.
    • Computed (AlarmActor): the raise now compares the banded severity with the same 700 threshold. Priority 1–500 → Error, 501+ → Warning. Before, raw PriorityLevel >= 700 → Error. A computed alarm configured at priority 700–1000 used to log as Error and now logs as Warning. One at priority 1–500 used to log as Warning and now logs as Error.
  • Site scripts reading ScriptAlarm.Severity (Alarms.CurrentAsync() / MesReportableAsync()). They get the banded value. ScriptAlarm.Priority (new, trailing) carries exactly what Severity carried before.

MES 900–999 band: unchanged selection, but deployed scripts must move to Priority

The band keys off the raw priority. ScriptAlarm.Priority equals the pre-#129 Severity for every source and every input value; Priority_is_the_pre_129_Severity_so_the_MES_band_is_unchanged pins this. A script that tests a.Priority in 900–999 therefore selects exactly the alarms it selected before. A script that still tests a.Severity would select nothing, because banded values are only 250/500/750/1000 for MxAccess and computed rows. The MES API's Severity, MinSeverity and MaxSeverity are the raw 0–999 priority as well. The repo rig scripts (docker/seed-mes-alarm-demo.sh, docker/seed-mes-test-machine.sh) now read a.Priority, so their MES output is byte-for-byte what it was. docker/verify-*.sh needed no change.

Owner action before deploying this build anywhere. Any deployed site script that reads ScriptAlarm.Severity for MES must switch to ScriptAlarm.Priority in the same deploy. Known scripts:

  • CvdReactor.SimpleAlarmStatus / CvdReactor.AlarmStatus (the band predicate, IsFlaggedForMES, Severity = a.Severity)
  • DEV vd03's SBTEST01/02 AlarmStatus site script (re-run docker/seed-mes-test-machine.sh, or edit a.Severity → a.Priority in its three places)

Nothing is deployed yet: vd03 and QA run builds older than this one. Tracked as deploy gate #131.

Wire, persistence, mixed versions

  • No proto change. AlarmStateUpdate.priority keeps its pre-#129 meaning (the raw source number). Central derives Severity from it by kind (AlarmConditionStateFactory.SeverityFromPriority: banded for MxAccess and computed, verbatim for OPC UA). That is also correct for a site still on an older build. An old central reading a new site gets the old behaviour on the live stream.
  • Debug View snapshot from an un-upgraded site. The old site sends its old (raw) Severity explicitly in AlarmConditionStateDto, so its MxAccess rows show the raw number until the site is upgraded. Central and sites normally deploy together.
  • native_alarm_state rows written by an older build. These have no Priority. On rehydrate, NativeAlarmActor moves the old Severity into Priority and re-derives Severity (banded for MxAccess, kept for OPC UA). No migration is needed.

Deliberately unchanged

  • CLI debug snapshot "Priority" column (it prints the raw priority, correctly labelled).
  • AlarmContext.Priority for on-trigger scripts (raw).
  • Template/instance PriorityLevel authoring (UI, CLI, Transport).
  • AlarmStateBadges (it only displays Severity).
  • Notifications, Inbound API host code, Flow Debugger: none of these sort or filter alarms by severity.
## Consumer impact: alarm Severity now runs one direction (higher = more urgent) **This is a visible ordering flip** for anyone who reads `Severity` on an MxAccess Gateway native alarm or a computed alarm. OPC UA rows do not change. ### What a reader sees after this build | Source | `Severity` before | `Severity` now | Raw value now lives on | |---|---|---|---| | OPC UA native (incl. Galaxy via OtOpcUa) | Part 9 Severity, clamped 0–1000 | **unchanged** | `Priority` (= Severity; OPC UA has no separate priority) | | MxAccess Gateway native | AVEVA priority, clamped 0–1000 (1 = most urgent) | AVEVA band: 1–250 → **1000**, 251–500 → **750**, 501–750 → **500**, 751–999 → **250**. Out-of-range values clamp: 0 or less reads 1000, above 999 reads 250 | `Priority` (clamped 0–1000, i.e. the old Severity) | | Computed (`PriorityLevel`) | `PriorityLevel` verbatim | same band as above | `Priority` (`PriorityLevel` verbatim) | The table is `AlarmPriorityBands`. It matches OtOpcUa `GalaxyAlarmPriority` (lmxopcua#887, `b61f3e05`) value for value, edge rows and clamps included, so a Galaxy alarm now has the same Severity whether it arrives through OtOpcUa or straight from mxaccessgw. OtOpcUa's history-only "not an alarm" row (AVEVA *severity* 0 → 1) has no counterpart here. The live gateway feed carries only alarms and only their priority, and a computed alarm is always an alarm. Priority 0 therefore clamps to Critical, as OtOpcUa's `Map(0)` does. ### Who sees the flip - **Central UI Alarm Summary.** "Worst Severity" (max), the minimum-severity filter (`>=`) and the severity sort (descending by default) now put AVEVA priority 1 at the top. Before, they put priority 999 at the top. The numbers in the **Sev** column change for MxAccess and computed rows. - **Debug View / alarm badges (`sev N`).** These show the banded number. - **Site event log `alarm` category.** - Native (`NativeAlarmActor`): the threshold is unchanged (`Severity >= 700` → `Error`). An MxAccess priority 1–500 raise now logs `Error` and 501–999 logs `Warning`. Before it was the other way round. - Computed (`AlarmActor`): the raise now compares the banded severity with the same 700 threshold. Priority 1–500 → `Error`, 501+ → `Warning`. Before, raw `PriorityLevel >= 700` → `Error`. **A computed alarm configured at priority 700–1000 used to log as Error and now logs as Warning. One at priority 1–500 used to log as Warning and now logs as Error.** - **Site scripts reading `ScriptAlarm.Severity` (`Alarms.CurrentAsync()` / `MesReportableAsync()`).** They get the banded value. **`ScriptAlarm.Priority` (new, trailing) carries exactly what `Severity` carried before.** ### MES 900–999 band: unchanged selection, but deployed scripts must move to `Priority` The band keys off the raw priority. `ScriptAlarm.Priority` equals the pre-#129 `Severity` for every source and every input value; `Priority_is_the_pre_129_Severity_so_the_MES_band_is_unchanged` pins this. A script that tests `a.Priority` in 900–999 therefore selects exactly the alarms it selected before. **A script that still tests `a.Severity` would select nothing**, because banded values are only 250/500/750/1000 for MxAccess and computed rows. The MES API's `Severity`, `MinSeverity` and `MaxSeverity` are the raw 0–999 priority as well. The repo rig scripts (`docker/seed-mes-alarm-demo.sh`, `docker/seed-mes-test-machine.sh`) now read `a.Priority`, so their MES output is byte-for-byte what it was. `docker/verify-*.sh` needed no change. **Owner action before deploying this build anywhere.** Any deployed site script that reads `ScriptAlarm.Severity` for MES must switch to `ScriptAlarm.Priority` in the same deploy. Known scripts: - `CvdReactor.SimpleAlarmStatus` / `CvdReactor.AlarmStatus` (the band predicate, `IsFlaggedForMES`, `Severity = a.Severity`) - DEV vd03's SBTEST01/02 `AlarmStatus` site script (re-run `docker/seed-mes-test-machine.sh`, or edit `a.Severity` → `a.Priority` in its three places) Nothing is deployed yet: vd03 and QA run builds older than this one. Tracked as deploy gate #131. ### Wire, persistence, mixed versions - **No proto change.** `AlarmStateUpdate.priority` keeps its pre-#129 meaning (the raw source number). Central derives `Severity` from it by kind (`AlarmConditionStateFactory.SeverityFromPriority`: banded for MxAccess and computed, verbatim for OPC UA). That is also correct for a site still on an older build. An old central reading a new site gets the old behaviour on the live stream. - **Debug View snapshot from an un-upgraded site.** The old site sends its old (raw) `Severity` explicitly in `AlarmConditionStateDto`, so its MxAccess rows show the raw number until the site is upgraded. Central and sites normally deploy together. - **`native_alarm_state` rows written by an older build.** These have no `Priority`. On rehydrate, `NativeAlarmActor` moves the old Severity into `Priority` and re-derives `Severity` (banded for MxAccess, kept for OPC UA). No migration is needed. ### Deliberately unchanged - CLI `debug snapshot` "Priority" column (it prints the raw `priority`, correctly labelled). - `AlarmContext.Priority` for on-trigger scripts (raw). - Template/instance `PriorityLevel` authoring (UI, CLI, Transport). - `AlarmStateBadges` (it only displays `Severity`). - Notifications, Inbound API host code, Flow Debugger: none of these sort or filter alarms by severity.
Author
Owner

Fixed in dd4f1c2a (main, rebased on e36cd605). Owner decision, approved 2026-11-12: take the recommendation.

What changed

  1. One direction. AlarmConditionState.Severity is higher = more urgent for every kind. New Commons.Types.Alarms.AlarmPriorityBands.ToSeverity is OtOpcUa's GalaxyAlarmPriority.Map (lmxopcua#887, b61f3e05) value for value: 1–250 → 1000, 251–500 → 750, 501–750 → 500, 751–999 → 250, with ≤ 0 → 1000 and > 999 → 250. It is used by MxGatewayAlarmMapper (the gateway severity is the AVEVA priority) and by AlarmConditionStateFactory.ForComputed (PriorityLevel). The OPC UA path is unchanged. OtOpcUa's history-only "not an alarm" row (AVEVA severity 0 → 1) has no live counterpart, so it is not ported.
  2. Raw priority kept. AlarmConditionState.Priority (new, trailing int?), AlarmStateChanged.Priority and ScriptAlarm.Priority (new, trailing) all carry exactly the pre-#129 Severity: the AVEVA priority clamped to 0–1000, PriorityLevel, or Part 9 Severity for OPC UA. There is no proto change. AlarmStateUpdate.priority keeps its meaning, and central derives Severity by kind (SeverityFromPriority), so the decode is right for un-upgraded sites too. The Debug View snapshot decode restores Condition.Priority. NativeAlarmActor upgrades pre-#129 native_alarm_state rows on rehydrate.
  3. MES 900–999 band. It keys off Priority, never Severity. A test pins Priority == pre-#129 Severity for every input from int.MinValue to int.MaxValue, so the band selects identical alarms. The rig scripts docker/seed-mes-alarm-demo.sh and seed-mes-test-machine.sh now use a.Priority for the filters and the MES Severity field, so their output is unchanged. Deployed scripts must make the same switch: deploy gate #131.
  4. Consumers.
    • Changed: AlarmActor raise event-log severity is now banded severity ≥ 700 (was raw priority ≥ 700); SiteStreamGrpcClient.ConvertToDomainEvent; SiteCommandDtoMapper.FromProto; ScriptRuntimeContext.ToScriptAlarm; NativeAlarmActor (Emit/rehydrate).
    • Now correct with no code change: NativeAlarmActor.ErrorSeverityThreshold, AlarmSummaryService worst = max, AlarmSummary.razor >= filter and descending sort, AlarmStateBadges.
    • Deliberately left alone: CLI debug snapshot Priority column (raw, labelled), AlarmContext.Priority (raw), PriorityLevel authoring in UI/CLI/Transport. Notifications, Inbound API host code and Flow Debugger have no severity sort or filter.
  5. Docs: Component-Commons, -DataConnectionLayer, -SiteRuntime, -Communication, -CentralUI, -InboundAPI, HighLevelReqs, docs/plans/2026-06-30-mes-alarm-status-api.md, CLAUDE.md, CLI README.

Tests (Release, rebased tree): Commons 977/977, DataConnectionLayer 549/549, Communication 834/834, SiteRuntime 1055/1055, CentralUI (bUnit) 1545/1545, IntegrationTests SiteAlarmStream 2/2. One unrelated DCL flake (TagResolutionCounts_ConnectionLevelFailureThenRecovery…) failed once and passed on its own and in every later full run.

  • New coverage: the band table at every edge and clamp, monotonicity, MxGateway live and snapshot, the MES-band invariance, the stream decode per kind, Debug snapshot Priority, legacy-row rehydrate, the computed and native event-log thresholds, Alarm Summary worst/sort.
  • Mutation check: reverting ToSeverity to the old clamp turned 62 tests red across all five projects, bUnit included. Keying Priority off the new Severity turned the MES-band test (plus 3 others) red. Both were restored.

Consumer impact: see the previous comment. No deploys were made.

**Fixed in `dd4f1c2a`** (main, rebased on `e36cd605`). Owner decision, approved 2026-11-12: take the recommendation. **What changed** 1. **One direction.** `AlarmConditionState.Severity` is higher = more urgent for every kind. New `Commons.Types.Alarms.AlarmPriorityBands.ToSeverity` is OtOpcUa's `GalaxyAlarmPriority.Map` (lmxopcua#887, `b61f3e05`) value for value: 1–250 → 1000, 251–500 → 750, 501–750 → 500, 751–999 → 250, with ≤ 0 → 1000 and > 999 → 250. It is used by `MxGatewayAlarmMapper` (the gateway `severity` is the AVEVA priority) and by `AlarmConditionStateFactory.ForComputed` (`PriorityLevel`). The OPC UA path is unchanged. OtOpcUa's history-only "not an alarm" row (AVEVA severity 0 → 1) has no live counterpart, so it is not ported. 2. **Raw priority kept.** `AlarmConditionState.Priority` (new, trailing `int?`), `AlarmStateChanged.Priority` and `ScriptAlarm.Priority` (new, trailing) all carry exactly the pre-#129 Severity: the AVEVA priority clamped to 0–1000, `PriorityLevel`, or Part 9 Severity for OPC UA. There is no proto change. `AlarmStateUpdate.priority` keeps its meaning, and central derives Severity by kind (`SeverityFromPriority`), so the decode is right for un-upgraded sites too. The Debug View snapshot decode restores `Condition.Priority`. `NativeAlarmActor` upgrades pre-#129 `native_alarm_state` rows on rehydrate. 3. **MES 900–999 band.** It keys off `Priority`, never `Severity`. A test pins `Priority` == pre-#129 `Severity` for every input from `int.MinValue` to `int.MaxValue`, so the band selects identical alarms. The rig scripts `docker/seed-mes-alarm-demo.sh` and `seed-mes-test-machine.sh` now use `a.Priority` for the filters and the MES `Severity` field, so their output is unchanged. **Deployed scripts must make the same switch: deploy gate #131.** 4. **Consumers.** - Changed: `AlarmActor` raise event-log severity is now banded severity ≥ 700 (was raw priority ≥ 700); `SiteStreamGrpcClient.ConvertToDomainEvent`; `SiteCommandDtoMapper.FromProto`; `ScriptRuntimeContext.ToScriptAlarm`; `NativeAlarmActor` (Emit/rehydrate). - Now correct with no code change: `NativeAlarmActor.ErrorSeverityThreshold`, `AlarmSummaryService` worst = max, `AlarmSummary.razor` `>=` filter and descending sort, `AlarmStateBadges`. - Deliberately left alone: CLI `debug snapshot` Priority column (raw, labelled), `AlarmContext.Priority` (raw), `PriorityLevel` authoring in UI/CLI/Transport. Notifications, Inbound API host code and Flow Debugger have no severity sort or filter. 5. **Docs:** Component-Commons, -DataConnectionLayer, -SiteRuntime, -Communication, -CentralUI, -InboundAPI, HighLevelReqs, `docs/plans/2026-06-30-mes-alarm-status-api.md`, CLAUDE.md, CLI README. **Tests** (Release, rebased tree): Commons 977/977, DataConnectionLayer 549/549, Communication 834/834, SiteRuntime 1055/1055, CentralUI (bUnit) 1545/1545, IntegrationTests `SiteAlarmStream` 2/2. One unrelated DCL flake (`TagResolutionCounts_ConnectionLevelFailureThenRecovery…`) failed once and passed on its own and in every later full run. - New coverage: the band table at every edge and clamp, monotonicity, MxGateway live and snapshot, the MES-band invariance, the stream decode per kind, Debug snapshot `Priority`, legacy-row rehydrate, the computed and native event-log thresholds, Alarm Summary worst/sort. - **Mutation check:** reverting `ToSeverity` to the old clamp turned 62 tests red across all five projects, bUnit included. Keying `Priority` off the new Severity turned the MES-band test (plus 3 others) red. Both were restored. Consumer impact: see the previous comment. No deploys were made.
Sign in to join this conversation.