fix(ui): Alarm Summary poll defers row rebuilds to the live path while IsLive (plan R2-07 T8)

This commit is contained in:
Joseph Doherty
2026-07-13 10:57:09 -04:00
parent d33337a834
commit 925c869826
3 changed files with 53 additions and 7 deletions
@@ -298,9 +298,20 @@
{
return;
}
_rows = result.Alarms;
// _notReporting is the poll's unique authority — the alarm-only live cache
// cannot compute it — so it is always refreshed.
_notReporting = result.NotReportingInstances;
_rollup = AlarmSummaryService.ComputeRollup(_rows);
// While the cache is live, the live deltas own the row set: a poll whose fan-out
// started BEFORE a delta must not land after it and momentarily revert the alarm
// state (arch-review R2 N5). When not live (pre-seed / degraded stream / dead
// aggregator — see R2 N6), the poll remains the full-rebuild safety net.
if (!LiveAlarmCache.IsLive(siteId))
{
_rows = result.Alarms;
_rollup = AlarmSummaryService.ComputeRollup(_rows);
}
RecomputeVisibleRows();
}
catch
@@ -348,10 +359,11 @@
// aggregated alarm set changes. We rebuild _rows/_rollup/_visibleRows from the
// immutable live snapshot — but deliberately DO NOT touch _notReporting, since
// the alarm-only live cache can't compute it.
// • The 15s poll (RefreshAsync) still runs untouched: it is the authority for
// _notReporting and the safety net when the cache is not live (pre-seed or a
// degraded/failed stream — IsLive == false). When live, the poll simply
// re-affirms the same snapshot; the live path just makes updates arrive sooner.
// • The 15s poll (RefreshAsync) is the authority for _notReporting and the
// full-rebuild safety net ONLY while the cache is not live (pre-seed or a
// degraded/failed stream — IsLive == false); when live it deliberately leaves
// _rows to the delta path, so a slow fan-out can never revert a fresher live
// delta (arch-review R2 N5).
// • Both paths mutate shared state only via the Blazor dispatcher (the poll via
// its InvokeAsync callback, the live delta via OnLiveChanged's InvokeAsync), so
// they are serialized and never race. Each rebuild is an idempotent snapshot, so