chore(audit): ScadaBridge C7 — perf re-baseline + CollapseAuditLogToCanonical projection test + index-test fix + dead-cref cleanup (Task 2.5)

Perf re-baseline (HotPathLatencyTests): empirical p95 on Apple M-series Release
build: 4KB DetailsJson slow path ≈14 µs, small-DetailsJson no-redactors ≈2 µs,
true no-op fast path ≈0 µs. Thresholds updated: 200 µs / 30 µs / 5 µs (≈15×
headroom for contested CI runners). Old thresholds (50 µs / 10 µs) were set for
the pre-C3 typed-field path; canonical JSON parse+rewrite is empirically faster.
Adds a third test (Filter_Apply_NoDetailsJson_FastPath) that asserts same-instance
return on the DetailsJson-null + within-cap fast path. Env-var overrides retained.

CollapseAuditLogToCanonicalMigrationTests (new): three MSSQL-gated [SkippableFact]
tests verifying Action/Category/Outcome projection, NULL Actor, DetailsJson codec
round-trip, and all six persisted computed columns (Kind/Status/SourceSiteId/
ExecutionId/ParentExecutionId) for ApiOutbound, InboundAuthFailure, and Failed-
status rows.

AddAuditLogTableMigrationTests: rename CreatesFiveNamedIndexes →
CreatesNineNamedIndexes; expand coverage from 5 original indexes to all 9 named
non-clustered indexes present after CollapseAuditLogToCanonical (adds
IX_AuditLog_Execution, IX_AuditLog_ParentExecution, IX_AuditLog_Node_Occurred,
UX_AuditLog_EventId).

Dead-cref cleanup: zero references to the deleted IAuditPayloadFilter /
DefaultAuditPayloadFilter / SafeDefaultAuditPayloadFilter types remain in any
.cs file (source or test). 26 occurrences across 13 files replaced with correct
references to IAuditRedactor / ScadaBridgeAuditRedactor / SafeDefaultAuditRedactor
or reworded as plain prose.

Residual sweep: no unused transitional code found beyond the acknowledged
"C3 transitional shim" comments on IngestedAtUtc stamping (active code, not dead).
This commit is contained in:
Joseph Doherty
2026-06-02 14:59:23 -04:00
parent 68a6bd1720
commit 635461c0fd
20 changed files with 525 additions and 141 deletions
@@ -8,13 +8,11 @@ using Microsoft.Extensions.Logging;
namespace ZB.MOM.WW.ScadaBridge.AuditLog.Payload;
/// <summary>
/// Pure, stateless redaction + truncation primitives shared by the legacy
/// <see cref="DefaultAuditPayloadFilter"/> (which operates on the
/// <see cref="ZB.MOM.WW.ScadaBridge.Commons.Entities.Audit.AuditEvent"/> entity)
/// and the canonical <see cref="ScadaBridgeAuditRedactor"/> (which operates on
/// <c>ZB.MOM.WW.Audit.AuditEvent</c> + its <c>DetailsJson</c>). Extracted in
/// ScadaBridge audit re-architecture stage C2 (Task 2.5) so the byte-exact
/// redaction logic lives in ONE place and the two code paths can never drift.
/// Pure, stateless redaction + truncation primitives used by
/// <see cref="ZB.MOM.WW.ScadaBridge.AuditLog.Redaction.ScadaBridgeAuditRedactor"/>
/// (which operates on <c>ZB.MOM.WW.Audit.AuditEvent</c> + its <c>DetailsJson</c>).
/// Extracted in ScadaBridge audit re-architecture stage C2 (Task 2.5) so the
/// byte-exact redaction logic lives in ONE place.
/// </summary>
/// <remarks>
/// <para>
@@ -26,10 +24,11 @@ namespace ZB.MOM.WW.ScadaBridge.AuditLog.Payload;
/// any DI / health-metric coupling.
/// </para>
/// <para>
/// The regex CACHE and per-call options resolution deliberately stay in
/// <see cref="DefaultAuditPayloadFilter"/> — they carry per-instance state
/// (lazy compile, 100 ms compile budget, sentinel entries) that the safety-net
/// tests pin to that class. This helper only holds the stateless stages that
/// The regex CACHE and per-call options resolution live in
/// <see cref="ZB.MOM.WW.ScadaBridge.AuditLog.Payload.AuditRegexCache"/> /
/// <see cref="ZB.MOM.WW.ScadaBridge.AuditLog.Redaction.ScadaBridgeAuditRedactor"/>
/// — they carry per-instance state (lazy compile, 100 ms compile budget,
/// sentinel entries). This helper only holds the stateless stages that
/// operate once the compiled regex set / redact list / cap has already been
/// resolved.
/// </para>
@@ -5,12 +5,11 @@ using Microsoft.Extensions.Logging;
namespace ZB.MOM.WW.ScadaBridge.AuditLog.Payload;
/// <summary>
/// Per-instance compiled-regex cache for audit body / SQL-parameter redactors.
/// Extracted in ScadaBridge audit re-architecture stage C2 (Task 2.5) so the
/// legacy <see cref="DefaultAuditPayloadFilter"/> and the canonical
/// <see cref="ZB.MOM.WW.ScadaBridge.AuditLog.Redaction.ScadaBridgeAuditRedactor"/>
/// share the SAME compile rules (50 ms per-match timeout, 100 ms compile budget,
/// invalid-pattern sentinel) rather than duplicating the logic.
/// Per-instance compiled-regex cache for audit body / SQL-parameter redactors
/// used by <see cref="ZB.MOM.WW.ScadaBridge.AuditLog.Redaction.ScadaBridgeAuditRedactor"/>.
/// Extracted in ScadaBridge audit re-architecture stage C2 (Task 2.5) to
/// centralize compile rules (50 ms per-match timeout, 100 ms compile budget,
/// invalid-pattern sentinel).
/// </summary>
/// <remarks>
/// <para>
@@ -1,9 +1,9 @@
namespace ZB.MOM.WW.ScadaBridge.AuditLog.Payload;
/// <summary>
/// Counter sink invoked by <see cref="DefaultAuditPayloadFilter"/> every time
/// a redactor (header / body regex / SQL parameter) throws and the filter has
/// to over-redact the offending field with the
/// Counter sink invoked by <see cref="ZB.MOM.WW.ScadaBridge.AuditLog.Redaction.ScadaBridgeAuditRedactor"/>
/// every time a redactor (header / body regex / SQL parameter) throws and the
/// redactor has to over-redact the offending field with the
/// <c>&lt;redacted: redactor error&gt;</c> marker. Bundle C bridges this into
/// the Site Health Monitoring report payload as <c>AuditRedactionFailure</c>.
/// </summary>