docs(alarms): describe the real StreamAlarms open sequence, and close TST-16

The final integration review's non-blocker reservations, all documentation
or comment truth except one test arm.

The alarm feed opens provider_status -> snapshot_status -> cached
active_alarm -> snapshot_complete, which is what GatewayAlarmMonitor has
done since the snapshot_status frame landed. Two places still described
the old order: docs/Grpc.md said provider_status arrived *after* the
initial snapshot, contradicting its own snapshot_status section two
paragraphs down, and AlarmFeedMessage's leading proto comment named
neither status frame at all. Both now state the sequence the monitor
emits, so a client author reading either one gets the frame order right.

The proto comment change flows through the generated trees (Contracts,
Go, Java) and the client descriptor set; the Rust vendored copy stays
byte-identical to canonical. Python's generator does not carry proto
comments into its output, so it has no delta.

AlarmsHubPublisherTests' valueless-payload case covered snapshot_complete
and provider_status but not snapshot_status, leaving the newest arm
unpinned against the redaction switch that must ignore it. Added.

WnWrapAlarmConsumer's ack comment led with the 2026-05-01 reading that
-55 tracks the 8-arg overload, then refuted itself six lines later with
the 2026-08-18 probe. It now leads with the observation labelled as
narrower than it reads -- mirroring the correction already in
docs/AlarmClientDiscovery.md -- so the block argues one thing: the 6-arg
call site stays for parity, and rc semantics are per the probe. A
paragraph orphaned by an earlier splice is rewrapped. Comment interior
only; the file compiles on Windows.

TST-16 gets a dated closure note rather than a rewrite: the flag it
called dead was implemented 2026-08-18. GatewayDashboardDesign's /browse
paragraph gains the failed-read carve-out GatewayConfiguration already
documented, so the two agree that a failed read keeps its - placeholder.
This commit is contained in:
Joseph Doherty
2026-08-18 06:09:06 -04:00
parent a95e510015
commit c3c603f169
11 changed files with 83 additions and 41 deletions
@@ -28513,9 +28513,13 @@ namespace ZB.MOM.WW.MxGateway.Contracts.Proto {
}
/// <summary>
/// One message on the StreamAlarms feed. The stream opens with one
/// `active_alarm` per currently-active alarm, then a single
/// `snapshot_complete`, then a `transition` for every subsequent change.
/// One message on the StreamAlarms feed. The stream opens with a
/// `provider_status` (the current provider mode), then a `snapshot_status`
/// (whether the cached active set may be incomplete), then one `active_alarm`
/// per currently-active alarm, then a single `snapshot_complete`. After that
/// come the live frames: a `transition` for every subsequent alarm change,
/// interleaved with a further `provider_status` on each failover/failback and a
/// further `snapshot_status` on each change of the truncation verdict.
/// </summary>
[global::System.Diagnostics.DebuggerDisplayAttribute("{ToString(),nq}")]
public sealed partial class AlarmFeedMessage : pb::IMessage<AlarmFeedMessage>
@@ -1003,9 +1003,13 @@ message StreamAlarmsRequest {
string alarm_filter_prefix = 2;
}
// One message on the StreamAlarms feed. The stream opens with one
// `active_alarm` per currently-active alarm, then a single
// `snapshot_complete`, then a `transition` for every subsequent change.
// One message on the StreamAlarms feed. The stream opens with a
// `provider_status` (the current provider mode), then a `snapshot_status`
// (whether the cached active set may be incomplete), then one `active_alarm`
// per currently-active alarm, then a single `snapshot_complete`. After that
// come the live frames: a `transition` for every subsequent alarm change,
// interleaved with a further `provider_status` on each failover/failback and a
// further `snapshot_status` on each change of the truncation verdict.
message AlarmFeedMessage {
oneof payload {
// Part of the initial active-alarm snapshot (ConditionRefresh).
@@ -119,13 +119,21 @@ public sealed class AlarmsHubPublisherTests
},
};
AlarmFeedMessage snapshotStatus = new()
{
SnapshotStatus = new AlarmSnapshotStatus { Truncated = true },
};
CapturingHubContext hubContext = await RunPublisherAsync(
showTagValues: false,
snapshotComplete,
providerStatus);
providerStatus,
snapshotStatus);
Assert.Same(snapshotComplete, hubContext.Sent[0]);
Assert.Same(providerStatus, hubContext.Sent[1]);
Assert.Same(snapshotStatus, hubContext.Sent[2]);
Assert.True(hubContext.Sent[2].SnapshotStatus.Truncated);
}
/// <summary>
@@ -345,23 +345,28 @@ public sealed class WnWrapAlarmConsumer : IMxAccessAlarmConsumer
?? throw new InvalidOperationException(
"Cannot acknowledge: WnWrapAlarmConsumer was disposed or has not been subscribed yet.");
// Empirically (live dev-rig 2026-05-01): the IwwAlarmConsumer2
// 8-arg AlarmAckByName returns -55 on this AVEVA build (looks like
// a stub) and the legacy 6-arg IwwAlarmConsumer.AlarmAckByName
// returns 0, which is why the 6-arg overload is the one called here.
// A zero return is NOT evidence the acknowledgement was applied: the
// 2026-08-18 probe acked a real, freshly raised alarm six ways and
// watched the snapshot stay UNACK_ALM, OPERATOR_NAME stay empty, and
// the alarm extension's own .Acked attribute stay False for 16 s after
// each call. On that rig the ack is accepted and then inert, and the
// -55 tracks the consumer rather than the overload — both overloads
// return -55 against a SetXmlAlarmQuery-applied consumer and 0 against
// the ack-only one. Whether any wnwrap ack reaches the alarm-history
// path is therefore unverified; see docs/AlarmProbeFindings.md.
// Operator-domain
// and operator-full-name fields are accepted by the proto contract
// for forward-compat but are not propagated to AVEVA today —
// wrapped in the 6-arg call so domain/full-name go to the
// The original observation (live dev-rig 2026-05-01) was narrower than
// it read: the IwwAlarmConsumer2 8-arg AlarmAckByName returned -55
// "and looked like a stub" while the legacy 6-arg
// IwwAlarmConsumer.AlarmAckByName returned 0, which is how the 6-arg
// overload came to be the one called here. The 2026-08-18 probe
// corrected both halves. The -55 tracks the *consumer*, not the
// overload: both overloads return -55 against a
// SetXmlAlarmQuery-applied consumer and 0 against the ack-only one
// used above. And a zero return is NOT evidence the acknowledgement
// was applied — the probe acked a real, freshly raised alarm six ways
// and watched the snapshot stay UNACK_ALM, OPERATOR_NAME stay empty,
// and the alarm extension's own .Acked attribute stay False for 16 s
// after each call. On that rig the ack is accepted and then inert, so
// whether any wnwrap ack reaches the alarm-history path is
// unverified; see docs/AlarmProbeFindings.md and the 2026-08-18
// correction in docs/AlarmClientDiscovery.md. The 6-arg call site
// below therefore stays as-is for MXAccess parity — the choice is no
// longer justified by the rc, and rc semantics are per the probe.
//
// Operator-domain and operator-full-name fields are accepted by the
// proto contract for forward-compat but are not propagated to AVEVA
// today — wrapped in the 6-arg call so domain/full-name go to the
// alarm-history operator-name field via the szOprName parameter.
// Suppress unused-warning explicitly:
_ = ackOperatorDomain;