diff --git a/alog.md b/alog.md index 8a265315..f7818094 100644 --- a/alog.md +++ b/alog.md @@ -135,7 +135,7 @@ Single wide table, polymorphic by `Channel` + `Kind` discriminators, JSON payloa - `IX_AuditLog_Target_Occurred (Target, OccurredAtUtc)` — "what did we send to system X." - Partitioning by month on `OccurredAtUtc` from day one (purge becomes a partition switch instead of a delete storm). -**`Kind` values (flat — 17 discriminators across all channels; pinned by `AuditEnumTests`):** +**`Kind` values (flat — 21 discriminators across all channels; pinned by `AuditEnumTests`):** | Kind | Fires when | |---|---| @@ -156,6 +156,10 @@ Single wide table, polymorphic by `Channel` + `Kind` discriminators, JSON payloa | `SecuredWriteExpire` | A `Pending` secured write aged past its server-side TTL and was transitioned to `Expired` without executing — emitted by the system (no verifier). | | `ReconciliationAbandoned` | A reconciliation pull row failed to insert up to the permanent-abandon threshold and central advanced its cursor past it; one synthetic row so the loss is queryable in the Audit Log itself. | | `ManualFailover` | An administrator triggered a manual failover of the central pair from the Health page; one row per invocation, written BEFORE the graceful `Cluster.Leave` is issued. `Target` = the leaving node's address. | +| `RouteCall` | Central `Route.To(...).Call(...)` round-trip completed (success or failure). **Always-on** — one row per routed call regardless of tracing; `Extra.childExecutionId` = the site run's `ExecutionId`. Channel `ScriptTrace`. (Flow Debugger, 2026-09-05) | +| `ScriptRun` | A traced site script run completed/failed/timed out; `Extra.trigger*` carries the trigger for value-change runs. Channel `ScriptTrace`, traced executions only. | +| `AttributeWrite` | A traced script wrote an attribute (or a batch); `Extra.writes[]` lists each write's outcome. Channel `ScriptTrace`, traced executions only. | +| `AttributeWait` | A traced script's `WaitAsync`/`WaitForAsync` resolved (satisfied / timeout / cancelled / error) with the observed value. Channel `ScriptTrace`, traced executions only. | ### Site: `AuditLog` (SQLite) diff --git a/src/ZB.MOM.WW.ScadaBridge.Commons/Types/Enums/AuditChannel.cs b/src/ZB.MOM.WW.ScadaBridge.Commons/Types/Enums/AuditChannel.cs index 3e643b62..069d896d 100644 --- a/src/ZB.MOM.WW.ScadaBridge.Commons/Types/Enums/AuditChannel.cs +++ b/src/ZB.MOM.WW.ScadaBridge.Commons/Types/Enums/AuditChannel.cs @@ -21,5 +21,13 @@ public enum AuditChannel /// restarted the active node, which is exactly the kind of act an audit log exists to /// attribute. (decision 2026-07-22) /// - Cluster + Cluster, + + /// + /// Flow Debugger trace rows (design 2026-09-05): a script run, an attribute write, an + /// attribute wait or a central route call, emitted only for traced executions + /// (plus the always-on ). Appended last so stored + /// ordinals of existing channels never move. + /// + ScriptTrace } diff --git a/src/ZB.MOM.WW.ScadaBridge.Commons/Types/Enums/AuditKind.cs b/src/ZB.MOM.WW.ScadaBridge.Commons/Types/Enums/AuditKind.cs index e5ed83d0..4e86bacc 100644 --- a/src/ZB.MOM.WW.ScadaBridge.Commons/Types/Enums/AuditKind.cs +++ b/src/ZB.MOM.WW.ScadaBridge.Commons/Types/Enums/AuditKind.cs @@ -49,5 +49,17 @@ public enum AuditKind /// is issued (the acting node is usually not the one that goes away, but the audit must /// survive either outcome). (decision 2026-07-22) /// - ManualFailover + ManualFailover, + + /// Central Route.To().Call round-trip (always-on; channel). + RouteCall, + + /// One site script run completed/failed/timed out (traced executions only; ). + ScriptRun, + + /// One attribute write or batch write made by a traced script (traced executions only; ). + AttributeWrite, + + /// One WaitAsync/WaitForAsync outcome in a traced script (traced executions only; ). + AttributeWait } diff --git a/tests/ZB.MOM.WW.ScadaBridge.Commons.Tests/Audit/ScriptTraceEnumTests.cs b/tests/ZB.MOM.WW.ScadaBridge.Commons.Tests/Audit/ScriptTraceEnumTests.cs new file mode 100644 index 00000000..dcd159a7 --- /dev/null +++ b/tests/ZB.MOM.WW.ScadaBridge.Commons.Tests/Audit/ScriptTraceEnumTests.cs @@ -0,0 +1,31 @@ +using ZB.MOM.WW.ScadaBridge.Commons.Types.Audit; +using ZB.MOM.WW.ScadaBridge.Commons.Types.Enums; + +namespace ZB.MOM.WW.ScadaBridge.Commons.Tests.Audit; + +/// +/// Flow Debugger (design 2026-09-05): the ScriptTrace channel and its four kinds must +/// fit the central AuditLog.Category/Kind columns and never move existing ordinals. +/// +public class ScriptTraceEnumTests +{ + [Theory] + [InlineData(AuditKind.RouteCall)] + [InlineData(AuditKind.ScriptRun)] + [InlineData(AuditKind.AttributeWrite)] + [InlineData(AuditKind.AttributeWait)] + public void ScriptTraceKinds_FitTheCentralColumns(AuditKind kind) + { + // AuditLog.Category / Kind are nvarchar(32) (AuditLogEntityTypeConfiguration). + Assert.True(kind.ToString().Length <= 32); + Assert.True(AuditChannel.ScriptTrace.ToString().Length <= 32); + Assert.Equal($"ScriptTrace.{kind}", AuditFieldBuilders.BuildAction(AuditChannel.ScriptTrace, kind)); + } + + [Fact] + public void ScriptTrace_IsAppendedAfterCluster_SoExistingOrdinalsAreStable() + { + Assert.True((int)AuditChannel.ScriptTrace > (int)AuditChannel.Cluster); + Assert.True((int)AuditKind.RouteCall > (int)AuditKind.ManualFailover); + } +} diff --git a/tests/ZB.MOM.WW.ScadaBridge.Commons.Tests/Types/Enums/AuditEnumTests.cs b/tests/ZB.MOM.WW.ScadaBridge.Commons.Tests/Types/Enums/AuditEnumTests.cs index b2176564..6e57625f 100644 --- a/tests/ZB.MOM.WW.ScadaBridge.Commons.Tests/Types/Enums/AuditEnumTests.cs +++ b/tests/ZB.MOM.WW.ScadaBridge.Commons.Tests/Types/Enums/AuditEnumTests.cs @@ -12,7 +12,7 @@ public class AuditEnumTests [Fact] public void AuditChannel_HasExactlyExpectedMembers() { - var expected = new[] { "ApiOutbound", "DbOutbound", "Notification", "ApiInbound", "SecuredWrite", "Cluster" }; + var expected = new[] { "ApiOutbound", "DbOutbound", "Notification", "ApiInbound", "SecuredWrite", "Cluster", "ScriptTrace" }; var actual = Enum.GetValues(typeof(AuditChannel)) .Cast() .Select(x => x.ToString()) @@ -23,7 +23,7 @@ public class AuditEnumTests } [Fact] - public void AuditKind_HasExactlySeventeenExpectedMembers() + public void AuditKind_HasExactlyTwentyOneExpectedMembers() { var expected = new[] { @@ -34,13 +34,14 @@ public class AuditEnumTests "SecuredWriteExpire", "ReconciliationAbandoned", "ManualFailover", + "RouteCall", "ScriptRun", "AttributeWrite", "AttributeWait", }; var actual = Enum.GetValues(typeof(AuditKind)) .Cast() .Select(x => x.ToString()) .ToArray(); - Assert.Equal(17, actual.Length); + Assert.Equal(21, actual.Length); Assert.Equal(expected, actual); }