feat(alarms): thread isNative through MaterialiseAlarmCondition; node manager tracks native conditions [H6a]

This commit is contained in:
Joseph Doherty
2026-06-15 14:13:30 -04:00
parent ed941c51da
commit 418663b359
12 changed files with 82 additions and 18 deletions
@@ -323,6 +323,37 @@ public sealed class AlarmCommandRouterTests : IDisposable
await host.DisposeAsync();
}
/// <summary>H6a — a condition materialised with <c>isNative:true</c> is tracked so later inbound-ack
/// routing can dispatch its Acknowledge to the driver rather than the scripted engine.</summary>
[Fact]
public async Task Native_materialise_is_tracked_as_native()
{
var (host, server) = await BootAsync();
var nm = server.NodeManager!;
nm.EnsureFolder("eq", parentNodeId: null, displayName: "Equipment");
nm.MaterialiseAlarmCondition("a1", "eq", "d", "OffNormalAlarm", 700, isNative: true);
nm.IsNativeAlarmNode("a1").ShouldBeTrue();
await host.DisposeAsync();
}
/// <summary>H6a — a scripted condition (the default, <c>isNative:false</c>) is NOT tracked as native.</summary>
[Fact]
public async Task Scripted_materialise_is_not_native()
{
var (host, server) = await BootAsync();
var nm = server.NodeManager!;
nm.EnsureFolder("eq", parentNodeId: null, displayName: "Equipment");
nm.MaterialiseAlarmCondition("a2", "eq", "d", "OffNormalAlarm", 700, isNative: false);
nm.IsNativeAlarmNode("a2").ShouldBeFalse();
await host.DisposeAsync();
}
/// <summary>Builds a <see cref="ServerSystemContext"/> (an <see cref="ISessionOperationContext"/>)
/// carrying a <see cref="RoleCarryingUserIdentity"/> with the given name + roles — the exact seam the
/// gate reads via <c>(context as ISessionOperationContext)?.UserIdentity as RoleCarryingUserIdentity</c>.</summary>