diff --git a/src/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer/NativeAlarmAck.cs b/src/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer/NativeAlarmAck.cs
index 7595ebd2..a3a9842b 100644
--- a/src/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer/NativeAlarmAck.cs
+++ b/src/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer/NativeAlarmAck.cs
@@ -12,9 +12,9 @@ namespace ZB.MOM.WW.OtOpcUa.OpcUaServer;
/// ref, the operator's acknowledge comment, and the authenticated operator's display name.
///
///
-/// The acknowledged condition's folder-scoped NodeId identifier string
-/// (the same value the DriverHostActor inverse map keys native conditions by), used to resolve the
-/// command back to its backing driver + alarm ref.
+/// The folder-scoped condition NodeId identifier string — the same value
+/// stored in DriverHostActor._alarmNodeIdByDriverRef (keyed by (DriverInstanceId, FullName)),
+/// used to resolve the ack back to its backing driver.
/// The operator's acknowledge comment text, or null when none was supplied.
/// The authenticated operator's display name (empty when none resolves).
public sealed record NativeAlarmAck(string ConditionNodeId, string? Comment, string OperatorUser);
diff --git a/tests/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer.Tests/AlarmCommandRouterTests.cs b/tests/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer.Tests/AlarmCommandRouterTests.cs
index c2fbb8a2..cf683ac4 100644
--- a/tests/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer.Tests/AlarmCommandRouterTests.cs
+++ b/tests/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer.Tests/AlarmCommandRouterTests.cs
@@ -480,6 +480,35 @@ public sealed class AlarmCommandRouterTests : IDisposable
await host.DisposeAsync();
}
+ /// H6c — a NATIVE condition's Acknowledge from a non-null identity that lacks the
+ /// AlarmAck role is vetoed (BadUserAccessDenied) and NEITHER router is invoked — the gate
+ /// fails closed before any route, exactly as for scripted conditions.
+ [Fact]
+ public async Task Native_OnAcknowledge_without_AlarmAck_returns_denied_and_routes_nothing()
+ {
+ var (host, server) = await BootAsync();
+ var nm = server.NodeManager!;
+
+ var scripted = new List();
+ var native = new List();
+ nm.AlarmCommandRouter = scripted.Add;
+ nm.NativeAlarmAckRouter = native.Add;
+
+ nm.EnsureFolder("eq-nak4", parentNodeId: null, displayName: "Equipment NAK4");
+ nm.MaterialiseAlarmCondition("alm-nak4", "eq-nak4", "HighTemp", "OffNormalAlarm", severity: 700, isNative: true);
+ var condition = nm.TryGetAlarmCondition("alm-nak4");
+ condition.ShouldNotBeNull();
+
+ var ctx = SessionContext(server, "pete", "ReadOnly"); // non-null identity, but no AlarmAck
+ var result = condition!.OnAcknowledge!(ctx, condition, EventIdBytes(), new LocalizedText("nope"));
+
+ result.StatusCode.Code.ShouldBe(StatusCodes.BadUserAccessDenied);
+ native.ShouldBeEmpty();
+ scripted.ShouldBeEmpty();
+
+ await host.DisposeAsync();
+ }
+
/// H6c — a NATIVE condition's Acknowledge from an anonymous / role-less identity is vetoed
/// (BadUserAccessDenied) and NEITHER router is invoked — the gate fails closed before any route.
[Fact]