diff --git a/src/Server/ZB.MOM.WW.OtOpcUa.Runtime/Communication/NodeCommunicationActor.cs b/src/Server/ZB.MOM.WW.OtOpcUa.Runtime/Communication/NodeCommunicationActor.cs
index f8b7d5fd..88702dc8 100644
--- a/src/Server/ZB.MOM.WW.OtOpcUa.Runtime/Communication/NodeCommunicationActor.cs
+++ b/src/Server/ZB.MOM.WW.OtOpcUa.Runtime/Communication/NodeCommunicationActor.cs
@@ -128,8 +128,9 @@ public sealed class NodeCommunicationActor : ReceiveActor
}
// Send, not SendToAll: the deploy coordinator is a single singleton, and the central comm
- // actor forwards to its proxy. SendToAll would deliver one ack per central node, and the
- // coordinator would count the same node twice.
+ // actor forwards to its proxy. Its NodeId-keyed upsert absorbs a duplicate idempotently,
+ // so single-target is defence in depth and a bound on wasted sends — mirroring the
+ // corrected Grpc-branch rationale above.
_centralClient.Tell(new ClusterClient.Send(MeshPaths.CentralCommunication, ack));
}
}
diff --git a/tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests/Communication/GrpcApplyAckSenderTests.cs b/tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests/Communication/GrpcApplyAckSenderTests.cs
index 7974ae99..bd6603f8 100644
--- a/tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests/Communication/GrpcApplyAckSenderTests.cs
+++ b/tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests/Communication/GrpcApplyAckSenderTests.cs
@@ -22,8 +22,9 @@ namespace ZB.MOM.WW.OtOpcUa.Runtime.Tests.Communication;
/// The load-bearing fact is that ONE endpoint is dialled on success. This replaces an
/// Akka ClusterClient.Send, which is single-target at-most-once — not a
/// SendToAll. Both central nodes route an ack to the same
-/// ConfigPublishCoordinator singleton proxy, so posting to both would have the
-/// coordinator count this node twice and could seal a deployment on a doubled quorum.
+/// ConfigPublishCoordinator singleton proxy, whose NodeId-keyed upsert absorbs a
+/// duplicate idempotently — so the single-endpoint rule is defence in depth and a bound
+/// on wasted dials, not what protects the seal.
///
///
/// The failure side matters just as much: the receptionist dropped an undeliverable ack in
diff --git a/tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests/Communication/NodeCommunicationActorTests.cs b/tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests/Communication/NodeCommunicationActorTests.cs
index fbbe0003..473d2d02 100644
--- a/tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests/Communication/NodeCommunicationActorTests.cs
+++ b/tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests/Communication/NodeCommunicationActorTests.cs
@@ -94,8 +94,9 @@ public class NodeCommunicationActorTests : RuntimeActorTestBase
public void ApplyAck_goes_to_central_as_a_Send_not_a_SendToAll()
{
// Send, not SendToAll: the deploy coordinator is one singleton behind the central comm
- // actor's proxy. SendToAll would deliver one ack per central node and the coordinator would
- // count this node twice.
+ // actor's proxy. The coordinator's NodeId-keyed upsert absorbs a duplicate idempotently,
+ // so the single-target rule is defence in depth and a bound on wasted work, not a
+ // correctness requirement.
var client = CreateTestProbe();
var actor = Sys.ActorOf(NodeCommunicationActor.Props(client.Ref));
@@ -161,8 +162,9 @@ public class NodeCommunicationActorTests : RuntimeActorTestBase
public void A_sender_wins_over_a_cluster_client_and_the_ack_is_not_sent_twice()
{
// Defence in depth. The registration arm builds exactly one transport — under Grpc no
- // ClusterClient is created at all — but if both were ever present, posting over both would
- // deliver two acks for one apply and the coordinator would count this node twice.
+ // ClusterClient is created at all — and if both were ever present, posting over both would
+ // deliver two acks for one apply. The coordinator would absorb the duplicate (NodeId-keyed
+ // upsert), so this pins tidiness and bounded work, not seal correctness.
var client = CreateTestProbe();
var sender = new RecordingAckSender();
var actor = Sys.ActorOf(NodeCommunicationActor.Props(client.Ref, sender));