using ScadaLink.AuditLog.Payload; namespace ScadaLink.AuditLog.Central; /// /// Audit Log (#23) M6 Bundle E (T9) — bridges /// (incremented by /// every time a header / body / SQL /// parameter redactor stage throws and the filter has to over-redact the /// offending field) into so the /// failure surfaces on the central health surface as /// AuditCentralHealthSnapshot.AuditRedactionFailure. /// /// /// /// Site vs central. M5 Bundle C wired the SITE-side bridge /// (), /// which routes increments into the site health report payload's /// AuditRedactionFailure field. That handles redactor failures on the /// site SQLite hot-path (FallbackAuditWriter). M6 Bundle E (T9) adds the /// MIRROR bridge here so the same payload filter — when it runs on the /// central / /// paths — surfaces its failures on the /// central dashboard rather than disappearing into a NoOp. /// /// /// Registration shape. Site composition roots call /// , /// which overrides the binding with the site bridge. Central composition /// roots call , /// which overrides with this central bridge. A node never wears both hats — /// site and central are distinct host roles — so the two bridges never /// fight over the same binding at runtime. /// /// /// Why not a thin wrapper around the snapshot directly? The snapshot /// itself could be the bound implementation (it already implements /// ), but a dedicated class makes /// the central-vs-site asymmetry explicit at the DI boundary — readers of /// /// see "site → site bridge, central → central bridge", matching the /// /// shape one-for-one. /// /// public sealed class CentralAuditRedactionFailureCounter : IAuditRedactionFailureCounter { private readonly AuditCentralHealthSnapshot _snapshot; public CentralAuditRedactionFailureCounter(AuditCentralHealthSnapshot snapshot) { _snapshot = snapshot ?? throw new ArgumentNullException(nameof(snapshot)); } /// public void Increment() => ((IAuditRedactionFailureCounter)_snapshot).Increment(); }