fix(GWC-26): attach the alarm monitor's lease before SubscribeAlarms
RunMonitorAsync issued SubscribeAlarms and the first reconcile before the internal distributor subscriber was attached (via ISessionManager .ReadAlarmEventsAsync). The pump has been running since MarkReady started the dashboard mirror and only fans to subscribers registered at fan-out time, so every transition raised in that two-round-trip window bypassed the alarm feed — and a missed Acknowledge was never repaired, because ApplyReconcile broadcast presence deltas only. - The monitor now takes the internal lease directly from its session BEFORE SubscribeAlarms and drains it after the first reconcile; window transitions buffer in the lease's bounded channel. Processing them after ApplyReconcile is order-safe (ApplyTransition handles alarms the snapshot already placed). - ISessionManager.ReadAlarmEventsAsync removed — zero remaining callers. - ApplyReconcile broadcasts an Acknowledge feed transition when a both-present alarm's state advanced to ActiveAcked. This is a feed-level repair on the AlarmFeedMessage/StreamAlarms surface rebuilt from the worker's own snapshot, not MxEvent emission, so the "never synthesize events" rule is untouched; the reasoning is recorded on ApplyReconcile. The alarm-monitor test fakes now hand the monitor a real Ready GatewaySession with a dashboard mirror, which is what makes the window reproducible. Docs: docs/Sessions.md and gateway.md alarm-monitor ordering notes. Refs: archreview/2026-07-12/remediation/10-gateway-core.md GWC-26
This commit is contained in:
+14
@@ -154,6 +154,20 @@ session. The worker event channel is single-reader and asserts it (a second
|
||||
`WorkerClient.ReadEventsAsync` consumer throws), so a regression cannot silently
|
||||
split the event stream between two drains.
|
||||
|
||||
The monitor takes that internal lease **before** it sends `SubscribeAlarms`, and
|
||||
drains it after the first reconcile. The pump is already running by then (the
|
||||
dashboard mirror starts it at `MarkReady`) and the distributor fans only to
|
||||
subscribers registered at fan-out time, so attaching after the subscribe +
|
||||
reconcile round trips would drop every transition raised in that window —
|
||||
including an `Acknowledge`, which the presence-only reconcile deltas would never
|
||||
repair. Transitions arriving during the window buffer in the lease's bounded
|
||||
channel instead. As defense in depth for any window this ordering cannot cover
|
||||
(worker restart, internal-subscriber overflow disconnect), a reconcile that finds
|
||||
a known alarm now reported `ActiveAcked` broadcasts an `Acknowledge` transition on
|
||||
the alarm feed. That is a feed-level repair rebuilt from the worker's own
|
||||
snapshot on the `StreamAlarms` surface — it is not an `MxEvent` and never reaches
|
||||
`StreamEvents`, so the "never synthesize events" rule is untouched.
|
||||
|
||||
### Alarm providers and failover
|
||||
|
||||
The alarm feed has two providers, both implemented worker-side:
|
||||
|
||||
Reference in New Issue
Block a user