From 2c45c3238b437687b84756039d98ef614530dd1b Mon Sep 17 00:00:00 2001 From: Joseph Doherty Date: Thu, 9 Jul 2026 08:21:41 -0400 Subject: [PATCH] feat(site-call-audit): relay Retry/Discard emit operator-identity audit rows MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Additive RequestedBy on Retry/DiscardSiteCallRequest, plumbed from the Central UI. On an Applied relay, SiteCallAuditActor emits one best-effort CachedResolve central direct-write row (Submitted/Discarded) with the operator as Actor. Gated on an injected ICentralAuditWriter (null in existing tests → no mirror read, unchanged); the site remains the source of truth for the state change. --- docs/requirements/Component-SiteCallAudit.md | 11 ++ .../Pages/SiteCalls/SiteCallsReport.razor.cs | 8 +- .../Messages/Audit/SiteCallRelayMessages.cs | 11 +- .../SiteCallAuditActor.cs | 149 +++++++++++++++-- .../SiteCallRelayTests.cs | 155 ++++++++++++++++++ 5 files changed, 319 insertions(+), 15 deletions(-) diff --git a/docs/requirements/Component-SiteCallAudit.md b/docs/requirements/Component-SiteCallAudit.md index 26f2f263..63008751 100644 --- a/docs/requirements/Component-SiteCallAudit.md +++ b/docs/requirements/Component-SiteCallAudit.md @@ -107,6 +107,17 @@ applies the change and emits telemetry reflecting the new state; central never mutates the `SiteCalls` row directly. If the site is offline the command fails fast and the UI surfaces a "site unreachable" message. +On a **successful** relay (the site acks `Applied`), `SiteCallAuditActor` emits +one best-effort central **direct-write** audit row (`CachedResolve`, status +`Submitted` for a Retry / `Discarded` for a Discard) carrying the authenticated +operator as `Actor` and the `TrackedOperationId` as `CorrelationId` — recording +*who asked*. The operator identity flows in on `RetrySiteCallRequest` / +`DiscardSiteCallRequest` (`RequestedBy`, captured at the Central UI). This row +only adds provenance: the **site remains the source of truth** for the state +change itself, and central reads the stored `SiteCalls` row solely to enrich the +audit row's channel/target (a benign read, never a mutation). Audit is +best-effort — a writer fault never changes the relay outcome. + Only `Parked` rows are operator-actionable. `Failed` rows offer no Retry or Discard: a permanent failure (e.g. HTTP 4xx) would simply fail again, and the error was already returned synchronously to the calling script — there is diff --git a/src/ZB.MOM.WW.ScadaBridge.CentralUI/Components/Pages/SiteCalls/SiteCallsReport.razor.cs b/src/ZB.MOM.WW.ScadaBridge.CentralUI/Components/Pages/SiteCalls/SiteCallsReport.razor.cs index 2b38a4af..15e40e82 100644 --- a/src/ZB.MOM.WW.ScadaBridge.CentralUI/Components/Pages/SiteCalls/SiteCallsReport.razor.cs +++ b/src/ZB.MOM.WW.ScadaBridge.CentralUI/Components/Pages/SiteCalls/SiteCallsReport.razor.cs @@ -1,4 +1,5 @@ using Microsoft.AspNetCore.Components; +using Microsoft.AspNetCore.Components.Authorization; using Microsoft.AspNetCore.WebUtilities; using Microsoft.Extensions.Logging; using ZB.MOM.WW.ScadaBridge.CentralUI.Auth; @@ -48,6 +49,7 @@ public partial class SiteCallsReport [Inject] private NavigationManager Navigation { get; set; } = null!; [Inject] private SiteScopeService SiteScope { get; set; } = null!; + [Inject] private AuthenticationStateProvider AuthStateProvider { get; set; } = null!; // The Status filter