feat(audit-log): permanently-abandoned reconciliation rows leave a durable ReconciliationAbandoned audit record

When a pulled AuditEvent fails to insert on every retry up to the permanent-
abandon threshold, the reconciliation actor now writes ONE synthetic
ReconciliationAbandoned row (new AuditKind) alongside the Critical log line —
fresh EventId, Status=Failed, channel preserved from the lost row, Extra carrying
the abandoned EventId + source site + final error — via the same
ScadaBridgeAuditEventFactory the ingest path uses. Best-effort in its own
try/catch so it never blocks the cursor twice. The permanent loss is now
queryable in the Audit Log, not only in a rotating log file.
This commit is contained in:
Joseph Doherty
2026-07-09 08:50:44 -04:00
parent 20098c6108
commit 0385942c3f
4 changed files with 207 additions and 1 deletions
@@ -22,5 +22,15 @@ public enum AuditKind
SecuredWriteSubmit,
SecuredWriteApprove,
SecuredWriteReject,
SecuredWriteExecute
SecuredWriteExecute,
/// <summary>
/// A reconciliation pull row that failed to insert on every retry up to the
/// permanent-abandon threshold, so central advanced its cursor past it and
/// the row is permanently lost from the mirror. The reconciliation actor
/// emits ONE synthetic audit row of this kind (carrying the abandoned
/// <c>EventId</c>, source site, and final error) so the loss is queryable in
/// the Audit Log itself, not only in a rotating log file. (arch-review 04, Task 17)
/// </summary>
ReconciliationAbandoned
}