< Summary

Information
Class: AsiBackbone.Core.Audit.AuditResidue
Assembly: AsiBackbone.Core
File(s): /home/runner/work/AsiBackbone/AsiBackbone/src/AsiBackbone.Core/Audit/AuditResidue.cs
Line coverage
99%
Covered lines: 297
Uncovered lines: 2
Coverable lines: 299
Total lines: 699
Line coverage: 99.3%
Branch coverage
97%
Covered branches: 72
Total branches: 74
Branch coverage: 97.2%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.cctor()100%11100%
.ctor(...)100%11100%
get_EventId()100%11100%
get_AuditResidueId()100%11100%
get_SchemaVersion()100%11100%
get_OccurredUtc()100%11100%
get_ActorId()100%11100%
get_ActorType()100%11100%
get_ActorDisplayName()100%11100%
get_OperationName()100%11100%
get_Outcome()100%11100%
get_ReasonCodes()100%11100%
get_CorrelationId()100%11100%
get_TraceId()100%11100%
get_SpanId()100%11100%
get_ParentSpanId()100%11100%
get_DecisionLatencyMs()100%11100%
get_ConstraintSetHash()100%11100%
get_ConstraintCount()100%11100%
get_RiskScore()100%11100%
get_PolicyScope()100%11100%
get_TenantHash()100%11100%
get_OrganizationHash()100%11100%
get_EmitterStatus()100%11100%
get_EmitterProvider()100%11100%
get_OutboxSequence()100%11100%
get_GatewayExecutionId()100%11100%
get_DecisionStage()100%11100%
get_PolicyVersion()100%11100%
get_PolicyHash()100%11100%
get_Metadata()100%11100%
get_HasReasonCodes()100%11100%
get_HasMetadata()100%11100%
Create(...)100%11100%
FromDecision(...)100%11100%
FromConstraint(...)100%11100%
CreateCore(...)100%44100%
NormalizeIdentifier(...)100%22100%
NormalizeAuditResidueId(...)100%22100%
NormalizeOptional(...)100%22100%
NormalizeNonNegative(...)100%22100%
NormalizeNonNegative(...)100%22100%
NormalizeRiskScore(...)100%88100%
NormalizeReasonCodes(...)100%1818100%
UseTrustedReasonCodes(...)100%22100%
AddNormalizedReasonCode(...)100%22100%
CreateReasonCodeCollection(...)100%44100%
GetOutcomeName(...)85.71%7790%
GetOutcomeName(...)80%5587.5%
NormalizeMetadata(...)100%1414100%

File(s)

/home/runner/work/AsiBackbone/AsiBackbone/src/AsiBackbone.Core/Audit/AuditResidue.cs

#LineLine coverage
 1using System.Collections.ObjectModel;
 2using AsiBackbone.Core.Actors;
 3using AsiBackbone.Core.Constraints;
 4using AsiBackbone.Core.Decisions;
 5using AsiBackbone.Core.Serialization;
 6
 7namespace AsiBackbone.Core.Audit;
 8
 9/// <summary>
 10/// Represents the framework-neutral audit residue produced by an AsiBackbone operation.
 11/// </summary>
 12public sealed class AuditResidue : IAsiBackboneAuditResidue
 13{
 14    private const string OutcomeAcknowledgmentRequired = "AcknowledgmentRequired";
 15    private const string OutcomeAllowed = "Allowed";
 16    private const string OutcomeDenied = "Denied";
 17    private const string OutcomeDeferred = "Deferred";
 18    private const string OutcomeEscalationRecommended = "EscalationRecommended";
 19    private const string OutcomeNotApplicable = "NotApplicable";
 20    private const string OutcomeWarning = "Warning";
 21
 922    private static readonly ReadOnlyCollection<string> EmptyReasonCodes =
 923        Array.AsReadOnly(Array.Empty<string>());
 24
 925    private static readonly IReadOnlyDictionary<string, string> EmptyMetadata =
 926        new ReadOnlyDictionary<string, string>(
 927            new Dictionary<string, string>(StringComparer.Ordinal));
 28
 25129    private AuditResidue(
 25130        string eventId,
 25131        string auditResidueId,
 25132        string schemaVersion,
 25133        DateTimeOffset occurredUtc,
 25134        string actorId,
 25135        AsiBackboneActorType actorType,
 25136        string? actorDisplayName,
 25137        string operationName,
 25138        string outcome,
 25139        IReadOnlyList<string> reasonCodes,
 25140        string? correlationId,
 25141        string? traceId,
 25142        string? spanId,
 25143        string? parentSpanId,
 25144        long? decisionLatencyMs,
 25145        string? constraintSetHash,
 25146        int? constraintCount,
 25147        double? riskScore,
 25148        string? policyScope,
 25149        string? tenantHash,
 25150        string? organizationHash,
 25151        string? emitterStatus,
 25152        string? emitterProvider,
 25153        long? outboxSequence,
 25154        string? gatewayExecutionId,
 25155        string? decisionStage,
 25156        string? policyVersion,
 25157        string? policyHash,
 25158        IReadOnlyDictionary<string, string> metadata)
 59    {
 25160        ArgumentException.ThrowIfNullOrWhiteSpace(eventId);
 25161        ArgumentException.ThrowIfNullOrWhiteSpace(auditResidueId);
 25162        ArgumentException.ThrowIfNullOrWhiteSpace(actorId);
 25163        ArgumentException.ThrowIfNullOrWhiteSpace(operationName);
 24564        ArgumentException.ThrowIfNullOrWhiteSpace(outcome);
 65
 23966        EventId = eventId.Trim();
 23967        AuditResidueId = auditResidueId.Trim();
 23968        SchemaVersion = AsiBackboneSchemaVersions.Normalize(schemaVersion);
 23969        OccurredUtc = occurredUtc.ToUniversalTime();
 23970        ActorId = actorId.Trim();
 23971        ActorType = actorType;
 23972        ActorDisplayName = NormalizeOptional(actorDisplayName);
 23973        OperationName = operationName.Trim();
 23974        Outcome = outcome.Trim();
 23975        ReasonCodes = reasonCodes;
 23976        CorrelationId = NormalizeOptional(correlationId);
 23977        TraceId = NormalizeOptional(traceId);
 23978        SpanId = NormalizeOptional(spanId);
 23979        ParentSpanId = NormalizeOptional(parentSpanId);
 23980        DecisionLatencyMs = NormalizeNonNegative(decisionLatencyMs, nameof(decisionLatencyMs));
 23381        ConstraintSetHash = NormalizeOptional(constraintSetHash);
 23382        ConstraintCount = NormalizeNonNegative(constraintCount, nameof(constraintCount));
 22783        RiskScore = NormalizeRiskScore(riskScore);
 21384        PolicyScope = NormalizeOptional(policyScope);
 21385        TenantHash = NormalizeOptional(tenantHash);
 21386        OrganizationHash = NormalizeOptional(organizationHash);
 21387        EmitterStatus = NormalizeOptional(emitterStatus);
 21388        EmitterProvider = NormalizeOptional(emitterProvider);
 21389        OutboxSequence = NormalizeNonNegative(outboxSequence, nameof(outboxSequence));
 20790        GatewayExecutionId = NormalizeOptional(gatewayExecutionId);
 20791        DecisionStage = NormalizeOptional(decisionStage);
 20792        PolicyVersion = NormalizeOptional(policyVersion);
 20793        PolicyHash = NormalizeOptional(policyHash);
 20794        Metadata = metadata;
 20795    }
 96
 97    /// <inheritdoc />
 14298    public string EventId { get; }
 99
 100    /// <inheritdoc />
 114101    public string AuditResidueId { get; }
 102
 103    /// <inheritdoc />
 106104    public string SchemaVersion { get; }
 105
 106    /// <inheritdoc />
 108107    public DateTimeOffset OccurredUtc { get; }
 108
 109    /// <inheritdoc />
 104110    public string ActorId { get; }
 111
 112    /// <inheritdoc />
 96113    public AsiBackboneActorType ActorType { get; }
 114
 115    /// <inheritdoc />
 100116    public string? ActorDisplayName { get; }
 117
 118    /// <inheritdoc />
 112119    public string OperationName { get; }
 120
 121    /// <inheritdoc />
 140122    public string Outcome { get; }
 123
 124    /// <inheritdoc />
 194125    public IReadOnlyList<string> ReasonCodes { get; }
 126
 127    /// <inheritdoc />
 146128    public string? CorrelationId { get; }
 129
 130    /// <inheritdoc />
 140131    public string? TraceId { get; }
 132
 133    /// <inheritdoc />
 104134    public string? SpanId { get; }
 135
 136    /// <inheritdoc />
 104137    public string? ParentSpanId { get; }
 138
 139    /// <inheritdoc />
 100140    public long? DecisionLatencyMs { get; }
 141
 142    /// <inheritdoc />
 100143    public string? ConstraintSetHash { get; }
 144
 145    /// <inheritdoc />
 100146    public int? ConstraintCount { get; }
 147
 148    /// <inheritdoc />
 100149    public double? RiskScore { get; }
 150
 151    /// <inheritdoc />
 100152    public string? PolicyScope { get; }
 153
 154    /// <inheritdoc />
 100155    public string? TenantHash { get; }
 156
 157    /// <inheritdoc />
 100158    public string? OrganizationHash { get; }
 159
 160    /// <inheritdoc />
 104161    public string? EmitterStatus { get; }
 162
 163    /// <inheritdoc />
 104164    public string? EmitterProvider { get; }
 165
 166    /// <inheritdoc />
 104167    public long? OutboxSequence { get; }
 168
 169    /// <inheritdoc />
 104170    public string? GatewayExecutionId { get; }
 171
 172    /// <inheritdoc />
 104173    public string? DecisionStage { get; }
 174
 175    /// <inheritdoc />
 133176    public string? PolicyVersion { get; }
 177
 178    /// <inheritdoc />
 131179    public string? PolicyHash { get; }
 180
 181    /// <inheritdoc />
 203182    public IReadOnlyDictionary<string, string> Metadata { get; }
 183
 184    /// <summary>
 185    /// Gets a value indicating whether this audit residue contains reason codes.
 186    /// </summary>
 30187    public bool HasReasonCodes => ReasonCodes.Count > 0;
 188
 189    /// <summary>
 190    /// Gets a value indicating whether this audit residue contains metadata.
 191    /// </summary>
 20192    public bool HasMetadata => Metadata.Count > 0;
 193
 194    /// <summary>
 195    /// Creates audit residue from a host-defined operation outcome.
 196    /// </summary>
 197    /// <param name="actor">The actor associated with the operation.</param>
 198    /// <param name="operationName">The operation name.</param>
 199    /// <param name="outcome">The governance, constraint, or host-defined outcome.</param>
 200    /// <param name="reasonCodes">Optional machine-readable reason codes.</param>
 201    /// <param name="eventId">Optional audit event identifier. When omitted, a new identifier is generated.</param>
 202    /// <param name="occurredUtc">Optional event timestamp. When omitted, the current UTC timestamp is used.</param>
 203    /// <param name="correlationId">Optional correlation identifier.</param>
 204    /// <param name="traceId">Optional trace identifier.</param>
 205    /// <param name="policyVersion">Optional policy version.</param>
 206    /// <param name="policyHash">Optional policy hash.</param>
 207    /// <param name="metadata">Optional host-provided audit metadata.</param>
 208    /// <param name="auditResidueId">Optional audit residue identifier. When omitted, the normalized event identifier is
 209    /// <param name="spanId">Optional span identifier.</param>
 210    /// <param name="parentSpanId">Optional parent span identifier.</param>
 211    /// <param name="decisionLatencyMs">Optional decision latency in milliseconds.</param>
 212    /// <param name="constraintSetHash">Optional evaluated constraint-set hash.</param>
 213    /// <param name="constraintCount">Optional evaluated constraint count.</param>
 214    /// <param name="riskScore">Optional host-defined risk score.</param>
 215    /// <param name="policyScope">Optional host-defined policy scope.</param>
 216    /// <param name="tenantHash">Optional privacy-preserving tenant hash.</param>
 217    /// <param name="organizationHash">Optional privacy-preserving organization hash.</param>
 218    /// <param name="emitterStatus">Optional provider-neutral emitter status.</param>
 219    /// <param name="emitterProvider">Optional provider-neutral emitter provider name.</param>
 220    /// <param name="outboxSequence">Optional outbox sequence.</param>
 221    /// <param name="gatewayExecutionId">Optional gateway execution identifier.</param>
 222    /// <param name="decisionStage">Optional provider-neutral decision stage.</param>
 223    /// <param name="schemaVersion">Optional schema version. When omitted, the stable artifact schema version is used.</
 224    /// <returns>An audit residue value.</returns>
 225    public static AuditResidue Create(
 226        IAsiBackboneActorContext actor,
 227        string operationName,
 228        string outcome,
 229        IEnumerable<string>? reasonCodes = null,
 230        string? eventId = null,
 231        DateTimeOffset? occurredUtc = null,
 232        string? correlationId = null,
 233        string? traceId = null,
 234        string? policyVersion = null,
 235        string? policyHash = null,
 236        IReadOnlyDictionary<string, string>? metadata = null,
 237        string? auditResidueId = null,
 238        string? spanId = null,
 239        string? parentSpanId = null,
 240        long? decisionLatencyMs = null,
 241        string? constraintSetHash = null,
 242        int? constraintCount = null,
 243        double? riskScore = null,
 244        string? policyScope = null,
 245        string? tenantHash = null,
 246        string? organizationHash = null,
 247        string? emitterStatus = null,
 248        string? emitterProvider = null,
 249        long? outboxSequence = null,
 250        string? gatewayExecutionId = null,
 251        string? decisionStage = null,
 252        string? schemaVersion = null)
 253    {
 213254        return CreateCore(
 213255            actor,
 213256            operationName,
 213257            outcome,
 213258            NormalizeReasonCodes(reasonCodes),
 213259            eventId,
 213260            occurredUtc,
 213261            correlationId,
 213262            traceId,
 213263            policyVersion,
 213264            policyHash,
 213265            metadata,
 213266            auditResidueId,
 213267            spanId,
 213268            parentSpanId,
 213269            decisionLatencyMs,
 213270            constraintSetHash,
 213271            constraintCount,
 213272            riskScore,
 213273            policyScope,
 213274            tenantHash,
 213275            organizationHash,
 213276            emitterStatus,
 213277            emitterProvider,
 213278            outboxSequence,
 213279            gatewayExecutionId,
 213280            decisionStage,
 213281            schemaVersion);
 282    }
 283
 284    /// <summary>
 285    /// Creates audit residue from a governance decision.
 286    /// </summary>
 287    /// <param name="actor">The actor associated with the operation.</param>
 288    /// <param name="operationName">The operation name.</param>
 289    /// <param name="decision">The governance decision to audit.</param>
 290    /// <param name="eventId">Optional audit event identifier. When omitted, a new identifier is generated.</param>
 291    /// <param name="occurredUtc">Optional event timestamp. When omitted, the current UTC timestamp is used.</param>
 292    /// <param name="metadata">Optional host-provided audit metadata.</param>
 293    /// <param name="auditResidueId">Optional audit residue identifier. When omitted, the normalized event identifier is
 294    /// <param name="spanId">Optional span identifier.</param>
 295    /// <param name="parentSpanId">Optional parent span identifier.</param>
 296    /// <param name="decisionLatencyMs">Optional decision latency in milliseconds.</param>
 297    /// <param name="constraintSetHash">Optional evaluated constraint-set hash.</param>
 298    /// <param name="constraintCount">Optional evaluated constraint count.</param>
 299    /// <param name="riskScore">Optional host-defined risk score.</param>
 300    /// <param name="policyScope">Optional host-defined policy scope.</param>
 301    /// <param name="tenantHash">Optional privacy-preserving tenant hash.</param>
 302    /// <param name="organizationHash">Optional privacy-preserving organization hash.</param>
 303    /// <param name="emitterStatus">Optional provider-neutral emitter status.</param>
 304    /// <param name="emitterProvider">Optional provider-neutral emitter provider name.</param>
 305    /// <param name="outboxSequence">Optional outbox sequence.</param>
 306    /// <param name="gatewayExecutionId">Optional gateway execution identifier.</param>
 307    /// <param name="decisionStage">Optional provider-neutral decision stage.</param>
 308    /// <param name="schemaVersion">Optional schema version. When omitted, the stable artifact schema version is used.</
 309    /// <returns>An audit residue value.</returns>
 310    public static AuditResidue FromDecision(
 311        IAsiBackboneActorContext actor,
 312        string operationName,
 313        GovernanceDecision decision,
 314        string? eventId = null,
 315        DateTimeOffset? occurredUtc = null,
 316        IReadOnlyDictionary<string, string>? metadata = null,
 317        string? auditResidueId = null,
 318        string? spanId = null,
 319        string? parentSpanId = null,
 320        long? decisionLatencyMs = null,
 321        string? constraintSetHash = null,
 322        int? constraintCount = null,
 323        double? riskScore = null,
 324        string? policyScope = null,
 325        string? tenantHash = null,
 326        string? organizationHash = null,
 327        string? emitterStatus = null,
 328        string? emitterProvider = null,
 329        long? outboxSequence = null,
 330        string? gatewayExecutionId = null,
 331        string? decisionStage = null,
 332        string? schemaVersion = null)
 333    {
 28334        ArgumentNullException.ThrowIfNull(decision);
 335
 26336        return CreateCore(
 26337            actor,
 26338            operationName,
 26339            GetOutcomeName(decision.Outcome),
 26340            UseTrustedReasonCodes(decision.ReasonCodes),
 26341            eventId,
 26342            occurredUtc,
 26343            decision.CorrelationId,
 26344            decision.TraceId,
 26345            decision.PolicyVersion,
 26346            decision.PolicyHash,
 26347            metadata,
 26348            auditResidueId,
 26349            spanId,
 26350            parentSpanId,
 26351            decisionLatencyMs,
 26352            constraintSetHash,
 26353            constraintCount,
 26354            riskScore,
 26355            policyScope,
 26356            tenantHash,
 26357            organizationHash,
 26358            emitterStatus,
 26359            emitterProvider,
 26360            outboxSequence,
 26361            gatewayExecutionId,
 26362            decisionStage,
 26363            schemaVersion);
 364    }
 365
 366    /// <summary>
 367    /// Creates audit residue from a constraint evaluation result.
 368    /// </summary>
 369    /// <param name="actor">The actor associated with the operation.</param>
 370    /// <param name="operationName">The operation name.</param>
 371    /// <param name="constraintResult">The constraint evaluation result to audit.</param>
 372    /// <param name="eventId">Optional audit event identifier. When omitted, a new identifier is generated.</param>
 373    /// <param name="occurredUtc">Optional event timestamp. When omitted, the current UTC timestamp is used.</param>
 374    /// <param name="correlationId">Optional correlation identifier.</param>
 375    /// <param name="traceId">Optional trace identifier.</param>
 376    /// <param name="policyVersion">Optional policy version.</param>
 377    /// <param name="policyHash">Optional policy hash.</param>
 378    /// <param name="metadata">Optional host-provided audit metadata.</param>
 379    /// <param name="auditResidueId">Optional audit residue identifier. When omitted, the normalized event identifier is
 380    /// <param name="spanId">Optional span identifier.</param>
 381    /// <param name="parentSpanId">Optional parent span identifier.</param>
 382    /// <param name="decisionLatencyMs">Optional decision latency in milliseconds.</param>
 383    /// <param name="constraintSetHash">Optional evaluated constraint-set hash.</param>
 384    /// <param name="constraintCount">Optional evaluated constraint count.</param>
 385    /// <param name="riskScore">Optional host-defined risk score.</param>
 386    /// <param name="policyScope">Optional host-defined policy scope.</param>
 387    /// <param name="tenantHash">Optional privacy-preserving tenant hash.</param>
 388    /// <param name="organizationHash">Optional privacy-preserving organization hash.</param>
 389    /// <param name="emitterStatus">Optional provider-neutral emitter status.</param>
 390    /// <param name="emitterProvider">Optional provider-neutral emitter provider name.</param>
 391    /// <param name="outboxSequence">Optional outbox sequence.</param>
 392    /// <param name="gatewayExecutionId">Optional gateway execution identifier.</param>
 393    /// <param name="decisionStage">Optional provider-neutral decision stage.</param>
 394    /// <param name="schemaVersion">Optional schema version. When omitted, the stable artifact schema version is used.</
 395    /// <returns>An audit residue value.</returns>
 396    public static AuditResidue FromConstraint(
 397        IAsiBackboneActorContext actor,
 398        string operationName,
 399        ConstraintEvaluationResult constraintResult,
 400        string? eventId = null,
 401        DateTimeOffset? occurredUtc = null,
 402        string? correlationId = null,
 403        string? traceId = null,
 404        string? policyVersion = null,
 405        string? policyHash = null,
 406        IReadOnlyDictionary<string, string>? metadata = null,
 407        string? auditResidueId = null,
 408        string? spanId = null,
 409        string? parentSpanId = null,
 410        long? decisionLatencyMs = null,
 411        string? constraintSetHash = null,
 412        int? constraintCount = null,
 413        double? riskScore = null,
 414        string? policyScope = null,
 415        string? tenantHash = null,
 416        string? organizationHash = null,
 417        string? emitterStatus = null,
 418        string? emitterProvider = null,
 419        long? outboxSequence = null,
 420        string? gatewayExecutionId = null,
 421        string? decisionStage = null,
 422        string? schemaVersion = null)
 423    {
 16424        ArgumentNullException.ThrowIfNull(constraintResult);
 425
 14426        return CreateCore(
 14427            actor,
 14428            operationName,
 14429            GetOutcomeName(constraintResult.Outcome),
 14430            UseTrustedReasonCodes(constraintResult.ReasonCodes),
 14431            eventId,
 14432            occurredUtc,
 14433            correlationId,
 14434            traceId,
 14435            policyVersion,
 14436            policyHash,
 14437            metadata,
 14438            auditResidueId,
 14439            spanId,
 14440            parentSpanId,
 14441            decisionLatencyMs,
 14442            constraintSetHash,
 14443            constraintCount,
 14444            riskScore,
 14445            policyScope,
 14446            tenantHash,
 14447            organizationHash,
 14448            emitterStatus,
 14449            emitterProvider,
 14450            outboxSequence,
 14451            gatewayExecutionId,
 14452            decisionStage,
 14453            schemaVersion);
 454    }
 455
 456    private static AuditResidue CreateCore(
 457        IAsiBackboneActorContext actor,
 458        string operationName,
 459        string outcome,
 460        IReadOnlyList<string> reasonCodes,
 461        string? eventId,
 462        DateTimeOffset? occurredUtc,
 463        string? correlationId,
 464        string? traceId,
 465        string? policyVersion,
 466        string? policyHash,
 467        IReadOnlyDictionary<string, string>? metadata,
 468        string? auditResidueId,
 469        string? spanId,
 470        string? parentSpanId,
 471        long? decisionLatencyMs,
 472        string? constraintSetHash,
 473        int? constraintCount,
 474        double? riskScore,
 475        string? policyScope,
 476        string? tenantHash,
 477        string? organizationHash,
 478        string? emitterStatus,
 479        string? emitterProvider,
 480        long? outboxSequence,
 481        string? gatewayExecutionId,
 482        string? decisionStage,
 483        string? schemaVersion)
 484    {
 253485        ArgumentNullException.ThrowIfNull(actor);
 486
 251487        string normalizedEventId = NormalizeIdentifier(eventId);
 488
 251489        return new AuditResidue(
 251490            normalizedEventId,
 251491            NormalizeAuditResidueId(auditResidueId, normalizedEventId),
 251492            schemaVersion ?? AsiBackboneSchemaVersions.StableArtifactsV1,
 251493            occurredUtc ?? DateTimeOffset.UtcNow,
 251494            actor.ActorId,
 251495            actor.ActorType,
 251496            actor.DisplayName,
 251497            operationName,
 251498            outcome,
 251499            reasonCodes,
 251500            correlationId,
 251501            traceId,
 251502            spanId,
 251503            parentSpanId,
 251504            decisionLatencyMs,
 251505            constraintSetHash,
 251506            constraintCount,
 251507            riskScore,
 251508            policyScope,
 251509            tenantHash,
 251510            organizationHash,
 251511            emitterStatus,
 251512            emitterProvider,
 251513            outboxSequence,
 251514            gatewayExecutionId,
 251515            decisionStage,
 251516            policyVersion,
 251517            policyHash,
 251518            NormalizeMetadata(metadata));
 519    }
 520
 521    private static string NormalizeIdentifier(string? identifier)
 522    {
 251523        return string.IsNullOrWhiteSpace(identifier)
 251524            ? Guid.NewGuid().ToString("N")
 251525            : identifier.Trim();
 526    }
 527
 528    private static string NormalizeAuditResidueId(string? auditResidueId, string eventId)
 529    {
 251530        return string.IsNullOrWhiteSpace(auditResidueId)
 251531            ? eventId
 251532            : auditResidueId.Trim();
 533    }
 534
 535    private static string? NormalizeOptional(string? value)
 536    {
 3321537        return string.IsNullOrWhiteSpace(value)
 3321538            ? null
 3321539            : value.Trim();
 540    }
 541
 542    private static long? NormalizeNonNegative(long? value, string parameterName)
 543    {
 452544        return value < 0
 452545            ? throw new ArgumentOutOfRangeException(parameterName, value, "Value must be greater than or equal to zero."
 452546            : value;
 547    }
 548
 549    private static int? NormalizeNonNegative(int? value, string parameterName)
 550    {
 233551        return value < 0
 233552            ? throw new ArgumentOutOfRangeException(parameterName, value, "Value must be greater than or equal to zero."
 233553            : value;
 554    }
 555
 556    private static double? NormalizeRiskScore(double? riskScore)
 557    {
 227558        return riskScore is null
 227559            ? null
 227560            : double.IsNaN(riskScore.Value) || double.IsInfinity(riskScore.Value) || riskScore.Value < 0
 227561            ? throw new ArgumentOutOfRangeException(nameof(riskScore), riskScore, "Risk score must be a finite value gre
 227562            : riskScore;
 563    }
 564
 565    private static ReadOnlyCollection<string> NormalizeReasonCodes(IEnumerable<string>? reasonCodes)
 566    {
 213567        if (reasonCodes is null)
 568        {
 118569            return EmptyReasonCodes;
 570        }
 571
 95572        if (reasonCodes is ICollection<string> collection)
 573        {
 91574            if (collection.Count == 0)
 575            {
 21576                return EmptyReasonCodes;
 577            }
 578
 70579            string[] normalizedReasonCodes = new string[collection.Count];
 70580            int normalizedCount = 0;
 581
 368582            foreach (string? reasonCode in collection)
 583            {
 114584                AddNormalizedReasonCode(reasonCode, normalizedReasonCodes, ref normalizedCount);
 585            }
 586
 70587            return CreateReasonCodeCollection(normalizedReasonCodes, normalizedCount);
 588        }
 589
 4590        List<string>? normalizedList = null;
 591
 32592        foreach (string? reasonCode in reasonCodes)
 593        {
 12594            if (string.IsNullOrWhiteSpace(reasonCode))
 595            {
 596                continue;
 597            }
 598
 4599            normalizedList ??= [];
 4600            normalizedList.Add(reasonCode.Trim());
 601        }
 602
 4603        return normalizedList is null || normalizedList.Count == 0
 4604            ? EmptyReasonCodes
 4605            : Array.AsReadOnly([.. normalizedList]);
 606    }
 607
 608    private static IReadOnlyList<string> UseTrustedReasonCodes(IReadOnlyList<string> reasonCodes)
 609    {
 40610        return reasonCodes.Count == 0
 40611            ? EmptyReasonCodes
 40612            : reasonCodes;
 613    }
 614
 615    private static void AddNormalizedReasonCode(
 616        string? reasonCode,
 617        string[] normalizedReasonCodes,
 618        ref int normalizedCount)
 619    {
 114620        if (string.IsNullOrWhiteSpace(reasonCode))
 621        {
 20622            return;
 623        }
 624
 94625        normalizedReasonCodes[normalizedCount] = reasonCode.Trim();
 94626        normalizedCount++;
 94627    }
 628
 629    private static ReadOnlyCollection<string> CreateReasonCodeCollection(
 630        string[] normalizedReasonCodes,
 631        int normalizedCount)
 632    {
 70633        if (normalizedCount == 0)
 634        {
 2635            return EmptyReasonCodes;
 636        }
 637
 68638        if (normalizedCount == normalizedReasonCodes.Length)
 639        {
 58640            return Array.AsReadOnly(normalizedReasonCodes);
 641        }
 642
 10643        string[] filteredReasonCodes = new string[normalizedCount];
 10644        Array.Copy(normalizedReasonCodes, filteredReasonCodes, normalizedCount);
 645
 10646        return Array.AsReadOnly(filteredReasonCodes);
 647    }
 648
 649    private static string GetOutcomeName(GovernanceDecisionOutcome outcome)
 650    {
 26651        return outcome switch
 26652        {
 6653            GovernanceDecisionOutcome.Allowed => OutcomeAllowed,
 4654            GovernanceDecisionOutcome.Warning => OutcomeWarning,
 8655            GovernanceDecisionOutcome.Denied => OutcomeDenied,
 2656            GovernanceDecisionOutcome.Deferred => OutcomeDeferred,
 4657            GovernanceDecisionOutcome.AcknowledgmentRequired => OutcomeAcknowledgmentRequired,
 2658            GovernanceDecisionOutcome.EscalationRecommended => OutcomeEscalationRecommended,
 0659            _ => outcome.ToString()
 26660        };
 661    }
 662
 663    private static string GetOutcomeName(ConstraintEvaluationOutcome outcome)
 664    {
 14665        return outcome switch
 14666        {
 2667            ConstraintEvaluationOutcome.NotApplicable => OutcomeNotApplicable,
 2668            ConstraintEvaluationOutcome.Allowed => OutcomeAllowed,
 6669            ConstraintEvaluationOutcome.Warning => OutcomeWarning,
 4670            ConstraintEvaluationOutcome.Denied => OutcomeDenied,
 0671            _ => outcome.ToString()
 14672        };
 673    }
 674
 675    private static IReadOnlyDictionary<string, string> NormalizeMetadata(
 676        IReadOnlyDictionary<string, string>? metadata)
 677    {
 251678        if (metadata is null || metadata.Count == 0)
 679        {
 174680            return EmptyMetadata;
 681        }
 682
 77683        Dictionary<string, string> normalizedMetadata = new(metadata.Count, StringComparer.Ordinal);
 684
 404685        foreach (KeyValuePair<string, string> item in metadata)
 686        {
 125687            if (string.IsNullOrWhiteSpace(item.Key))
 688            {
 689                continue;
 690            }
 691
 111692            normalizedMetadata[item.Key.Trim()] = item.Value?.Trim() ?? string.Empty;
 693        }
 694
 77695        return normalizedMetadata.Count == 0
 77696            ? EmptyMetadata
 77697            : new ReadOnlyDictionary<string, string>(normalizedMetadata);
 698    }
 699}

Methods/Properties

.cctor()
.ctor(System.String,System.String,System.String,System.DateTimeOffset,System.String,AsiBackbone.Core.Actors.AsiBackboneActorType,System.String,System.String,System.String,System.Collections.Generic.IReadOnlyList`1<System.String>,System.String,System.String,System.String,System.String,System.Nullable`1<System.Int64>,System.String,System.Nullable`1<System.Int32>,System.Nullable`1<System.Double>,System.String,System.String,System.String,System.String,System.String,System.Nullable`1<System.Int64>,System.String,System.String,System.String,System.String,System.Collections.Generic.IReadOnlyDictionary`2<System.String,System.String>)
get_EventId()
get_AuditResidueId()
get_SchemaVersion()
get_OccurredUtc()
get_ActorId()
get_ActorType()
get_ActorDisplayName()
get_OperationName()
get_Outcome()
get_ReasonCodes()
get_CorrelationId()
get_TraceId()
get_SpanId()
get_ParentSpanId()
get_DecisionLatencyMs()
get_ConstraintSetHash()
get_ConstraintCount()
get_RiskScore()
get_PolicyScope()
get_TenantHash()
get_OrganizationHash()
get_EmitterStatus()
get_EmitterProvider()
get_OutboxSequence()
get_GatewayExecutionId()
get_DecisionStage()
get_PolicyVersion()
get_PolicyHash()
get_Metadata()
get_HasReasonCodes()
get_HasMetadata()
Create(AsiBackbone.Core.Actors.IAsiBackboneActorContext,System.String,System.String,System.Collections.Generic.IEnumerable`1<System.String>,System.String,System.Nullable`1<System.DateTimeOffset>,System.String,System.String,System.String,System.String,System.Collections.Generic.IReadOnlyDictionary`2<System.String,System.String>,System.String,System.String,System.String,System.Nullable`1<System.Int64>,System.String,System.Nullable`1<System.Int32>,System.Nullable`1<System.Double>,System.String,System.String,System.String,System.String,System.String,System.Nullable`1<System.Int64>,System.String,System.String,System.String)
FromDecision(AsiBackbone.Core.Actors.IAsiBackboneActorContext,System.String,AsiBackbone.Core.Decisions.GovernanceDecision,System.String,System.Nullable`1<System.DateTimeOffset>,System.Collections.Generic.IReadOnlyDictionary`2<System.String,System.String>,System.String,System.String,System.String,System.Nullable`1<System.Int64>,System.String,System.Nullable`1<System.Int32>,System.Nullable`1<System.Double>,System.String,System.String,System.String,System.String,System.String,System.Nullable`1<System.Int64>,System.String,System.String,System.String)
FromConstraint(AsiBackbone.Core.Actors.IAsiBackboneActorContext,System.String,AsiBackbone.Core.Constraints.ConstraintEvaluationResult,System.String,System.Nullable`1<System.DateTimeOffset>,System.String,System.String,System.String,System.String,System.Collections.Generic.IReadOnlyDictionary`2<System.String,System.String>,System.String,System.String,System.String,System.Nullable`1<System.Int64>,System.String,System.Nullable`1<System.Int32>,System.Nullable`1<System.Double>,System.String,System.String,System.String,System.String,System.String,System.Nullable`1<System.Int64>,System.String,System.String,System.String)
CreateCore(AsiBackbone.Core.Actors.IAsiBackboneActorContext,System.String,System.String,System.Collections.Generic.IReadOnlyList`1<System.String>,System.String,System.Nullable`1<System.DateTimeOffset>,System.String,System.String,System.String,System.String,System.Collections.Generic.IReadOnlyDictionary`2<System.String,System.String>,System.String,System.String,System.String,System.Nullable`1<System.Int64>,System.String,System.Nullable`1<System.Int32>,System.Nullable`1<System.Double>,System.String,System.String,System.String,System.String,System.String,System.Nullable`1<System.Int64>,System.String,System.String,System.String)
NormalizeIdentifier(System.String)
NormalizeAuditResidueId(System.String,System.String)
NormalizeOptional(System.String)
NormalizeNonNegative(System.Nullable`1<System.Int64>,System.String)
NormalizeNonNegative(System.Nullable`1<System.Int32>,System.String)
NormalizeRiskScore(System.Nullable`1<System.Double>)
NormalizeReasonCodes(System.Collections.Generic.IEnumerable`1<System.String>)
UseTrustedReasonCodes(System.Collections.Generic.IReadOnlyList`1<System.String>)
AddNormalizedReasonCode(System.String,System.String[],System.Int32&)
CreateReasonCodeCollection(System.String[],System.Int32)
GetOutcomeName(AsiBackbone.Core.Decisions.GovernanceDecisionOutcome)
GetOutcomeName(AsiBackbone.Core.Constraints.ConstraintEvaluationOutcome)
NormalizeMetadata(System.Collections.Generic.IReadOnlyDictionary`2<System.String,System.String>)