< Summary

Information
Class: AsiBackbone.Core.Decisions.GovernanceDecision
Assembly: AsiBackbone.Core
File(s): /home/runner/work/AsiBackbone/AsiBackbone/src/AsiBackbone.Core/Decisions/GovernanceDecision.cs
Line coverage
100%
Covered lines: 152
Uncovered lines: 0
Coverable lines: 152
Total lines: 514
Line coverage: 100%
Branch coverage
97%
Covered branches: 37
Total branches: 38
Branch coverage: 97.3%
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_Outcome()100%11100%
get_Reasons()100%11100%
get_ReasonCodes()100%11100%
get_CorrelationId()100%11100%
get_TraceId()100%11100%
get_PolicyVersion()100%11100%
get_PolicyHash()100%11100%
get_CanProceed()100%22100%
get_IsAllowed()100%11100%
get_IsWarning()100%11100%
get_IsDenied()100%11100%
get_IsDeferred()100%11100%
get_RequiresAcknowledgment()100%11100%
get_EscalationRecommended()100%11100%
get_HasReasons()100%11100%
Allow(...)100%11100%
Deny(...)100%11100%
Deny(...)100%11100%
Deny(...)100%11100%
Warning(...)100%11100%
Warning(...)100%11100%
Warning(...)100%11100%
Defer(...)100%11100%
RequireAcknowledgment(...)100%11100%
Escalate(...)100%11100%
NormalizeOptional(...)100%22100%
NormalizeTelemetryIdentifier(...)100%44100%
NormalizeReasons(...)93.75%1616100%
NormalizeReasonCollection(...)100%1010100%
CreateFallbackReason(...)100%11100%
CreateReadOnlyReason(...)100%11100%
CreateReasonCodes(...)100%44100%

File(s)

/home/runner/work/AsiBackbone/AsiBackbone/src/AsiBackbone.Core/Decisions/GovernanceDecision.cs

#LineLine coverage
 1using System.Collections.ObjectModel;
 2using AsiBackbone.Core.Results;
 3
 4namespace AsiBackbone.Core.Decisions;
 5
 6/// <summary>
 7/// Represents a framework-neutral governance decision produced by an AsiBackbone evaluation flow.
 8/// </summary>
 9public sealed class GovernanceDecision
 10{
 11    private const string DefaultDeniedCode = "decision.denied";
 12    private const string DefaultDeniedMessage = "Decision denied the operation.";
 13    private const string DefaultWarningCode = "decision.warning";
 14    private const string DefaultWarningMessage = "Decision produced a warning.";
 15
 16    /// <summary>
 17    /// Defines the maximum retained length for normalized correlation identifiers.
 18    /// </summary>
 19    /// <remarks>
 20    /// Correlation identifiers are trimmed before enforcement. Values longer than this limit are truncated to keep tele
 21    /// </remarks>
 22    public const int MaxCorrelationIdLength = 256;
 23
 24    /// <summary>
 25    /// Defines the maximum retained length for normalized trace identifiers.
 26    /// </summary>
 27    /// <remarks>
 28    /// Trace identifiers are trimmed before enforcement. Values longer than this limit are truncated to keep telemetry-
 29    /// </remarks>
 30    public const int MaxTraceIdLength = 256;
 31
 532    private static readonly ReadOnlyCollection<OperationReason> EmptyReasons =
 533        Array.AsReadOnly(Array.Empty<OperationReason>());
 34
 535    private static readonly ReadOnlyCollection<string> EmptyReasonCodes =
 536        Array.AsReadOnly(Array.Empty<string>());
 37
 81738    private GovernanceDecision(
 81739        GovernanceDecisionOutcome outcome,
 81740        IReadOnlyList<OperationReason> reasons,
 81741        string? correlationId,
 81742        string? traceId,
 81743        string? policyVersion,
 81744        string? policyHash)
 45    {
 81746        Outcome = outcome;
 81747        Reasons = reasons;
 81748        ReasonCodes = CreateReasonCodes(reasons);
 81749        CorrelationId = NormalizeTelemetryIdentifier(correlationId, MaxCorrelationIdLength);
 81750        TraceId = NormalizeTelemetryIdentifier(traceId, MaxTraceIdLength);
 81751        PolicyVersion = NormalizeOptional(policyVersion);
 81752        PolicyHash = NormalizeOptional(policyHash);
 81753    }
 54
 55    /// <summary>
 56    /// Gets the selected governance decision outcome.
 57    /// </summary>
 111558    public GovernanceDecisionOutcome Outcome { get; }
 59
 60    /// <summary>
 61    /// Gets reasons associated with the governance decision.
 62    /// </summary>
 47163    public IReadOnlyList<OperationReason> Reasons { get; }
 64
 65    /// <summary>
 66    /// Gets machine-readable reason codes associated with the governance decision.
 67    /// </summary>
 60668    public IReadOnlyList<string> ReasonCodes { get; }
 69
 70    /// <summary>
 71    /// Gets the correlation identifier associated with the decision, when supplied by the host.
 72    /// </summary>
 40773    public string? CorrelationId { get; }
 74
 75    /// <summary>
 76    /// Gets the trace identifier associated with the decision, when supplied by the host.
 77    /// </summary>
 18578    public string? TraceId { get; }
 79
 80    /// <summary>
 81    /// Gets the policy version associated with the decision, when supplied by the host.
 82    /// </summary>
 25283    public string? PolicyVersion { get; }
 84
 85    /// <summary>
 86    /// Gets the policy hash associated with the decision, when supplied by the host.
 87    /// </summary>
 25088    public string? PolicyHash { get; }
 89
 90    /// <summary>
 91    /// Gets a value indicating whether the decision allows immediate execution.
 92    /// </summary>
 24393    public bool CanProceed => Outcome is GovernanceDecisionOutcome.Allowed or GovernanceDecisionOutcome.Warning;
 94
 95    /// <summary>
 96    /// Gets a value indicating whether the decision allows the operation.
 97    /// </summary>
 11198    public bool IsAllowed => Outcome is GovernanceDecisionOutcome.Allowed;
 99
 100    /// <summary>
 101    /// Gets a value indicating whether the decision allows the operation with warnings.
 102    /// </summary>
 186103    public bool IsWarning => Outcome is GovernanceDecisionOutcome.Warning;
 104
 105    /// <summary>
 106    /// Gets a value indicating whether the decision denies the operation.
 107    /// </summary>
 202108    public bool IsDenied => Outcome is GovernanceDecisionOutcome.Denied;
 109
 110    /// <summary>
 111    /// Gets a value indicating whether the decision defers the operation.
 112    /// </summary>
 34113    public bool IsDeferred => Outcome is GovernanceDecisionOutcome.Deferred;
 114
 115    /// <summary>
 116    /// Gets a value indicating whether the decision requires acknowledgment before execution.
 117    /// </summary>
 57118    public bool RequiresAcknowledgment => Outcome is GovernanceDecisionOutcome.AcknowledgmentRequired;
 119
 120    /// <summary>
 121    /// Gets a value indicating whether the decision recommends escalation.
 122    /// </summary>
 22123    public bool EscalationRecommended => Outcome is GovernanceDecisionOutcome.EscalationRecommended;
 124
 125    /// <summary>
 126    /// Gets a value indicating whether the decision includes reason data.
 127    /// </summary>
 24128    public bool HasReasons => Reasons.Count > 0;
 129
 130    /// <summary>
 131    /// Creates an allowed governance decision.
 132    /// </summary>
 133    /// <param name="correlationId">Optional correlation identifier.</param>
 134    /// <param name="traceId">Optional trace identifier.</param>
 135    /// <param name="policyVersion">Optional policy version.</param>
 136    /// <param name="policyHash">Optional policy hash.</param>
 137    /// <returns>An allowed governance decision.</returns>
 138    public static GovernanceDecision Allow(
 139        string? correlationId = null,
 140        string? traceId = null,
 141        string? policyVersion = null,
 142        string? policyHash = null)
 143    {
 205144        return new GovernanceDecision(
 205145            GovernanceDecisionOutcome.Allowed,
 205146            EmptyReasons,
 205147            correlationId,
 205148            traceId,
 205149            policyVersion,
 205150            policyHash);
 151    }
 152
 153    /// <summary>
 154    /// Creates a denied governance decision.
 155    /// </summary>
 156    /// <param name="code">The machine-readable reason code.</param>
 157    /// <param name="message">The human-readable reason message.</param>
 158    /// <param name="correlationId">Optional correlation identifier.</param>
 159    /// <param name="traceId">Optional trace identifier.</param>
 160    /// <param name="policyVersion">Optional policy version.</param>
 161    /// <param name="policyHash">Optional policy hash.</param>
 162    /// <returns>A denied governance decision.</returns>
 163    public static GovernanceDecision Deny(
 164        string code,
 165        string message,
 166        string? correlationId = null,
 167        string? traceId = null,
 168        string? policyVersion = null,
 169        string? policyHash = null)
 170    {
 65171        return Deny(
 65172            OperationReason.Create(code, message),
 65173            correlationId,
 65174            traceId,
 65175            policyVersion,
 65176            policyHash);
 177    }
 178
 179    /// <summary>
 180    /// Creates a denied governance decision.
 181    /// </summary>
 182    /// <param name="reason">The reason associated with the denied decision.</param>
 183    /// <param name="correlationId">Optional correlation identifier.</param>
 184    /// <param name="traceId">Optional trace identifier.</param>
 185    /// <param name="policyVersion">Optional policy version.</param>
 186    /// <param name="policyHash">Optional policy hash.</param>
 187    /// <returns>A denied governance decision.</returns>
 188    public static GovernanceDecision Deny(
 189        OperationReason reason,
 190        string? correlationId = null,
 191        string? traceId = null,
 192        string? policyVersion = null,
 193        string? policyHash = null)
 194    {
 107195        ArgumentNullException.ThrowIfNull(reason);
 196
 105197        return new GovernanceDecision(
 105198            GovernanceDecisionOutcome.Denied,
 105199            CreateReadOnlyReason(reason),
 105200            correlationId,
 105201            traceId,
 105202            policyVersion,
 105203            policyHash);
 204    }
 205
 206    /// <summary>
 207    /// Creates a denied governance decision.
 208    /// </summary>
 209    /// <param name="reasons">The reasons associated with the denied decision.</param>
 210    /// <param name="correlationId">Optional correlation identifier.</param>
 211    /// <param name="traceId">Optional trace identifier.</param>
 212    /// <param name="policyVersion">Optional policy version.</param>
 213    /// <param name="policyHash">Optional policy hash.</param>
 214    /// <returns>A denied governance decision.</returns>
 215    public static GovernanceDecision Deny(
 216        IEnumerable<OperationReason> reasons,
 217        string? correlationId = null,
 218        string? traceId = null,
 219        string? policyVersion = null,
 220        string? policyHash = null)
 221    {
 128222        return new GovernanceDecision(
 128223            GovernanceDecisionOutcome.Denied,
 128224            NormalizeReasons(reasons, DefaultDeniedCode, DefaultDeniedMessage),
 128225            correlationId,
 128226            traceId,
 128227            policyVersion,
 128228            policyHash);
 229    }
 230
 231    /// <summary>
 232    /// Creates a warning governance decision.
 233    /// </summary>
 234    /// <param name="code">The machine-readable reason code.</param>
 235    /// <param name="message">The human-readable reason message.</param>
 236    /// <param name="correlationId">Optional correlation identifier.</param>
 237    /// <param name="traceId">Optional trace identifier.</param>
 238    /// <param name="policyVersion">Optional policy version.</param>
 239    /// <param name="policyHash">Optional policy hash.</param>
 240    /// <returns>A warning governance decision.</returns>
 241    public static GovernanceDecision Warning(
 242        string code,
 243        string message,
 244        string? correlationId = null,
 245        string? traceId = null,
 246        string? policyVersion = null,
 247        string? policyHash = null)
 248    {
 21249        return Warning(
 21250            OperationReason.Create(code, message),
 21251            correlationId,
 21252            traceId,
 21253            policyVersion,
 21254            policyHash);
 255    }
 256
 257    /// <summary>
 258    /// Creates a warning governance decision.
 259    /// </summary>
 260    /// <param name="reason">The reason associated with the warning decision.</param>
 261    /// <param name="correlationId">Optional correlation identifier.</param>
 262    /// <param name="traceId">Optional trace identifier.</param>
 263    /// <param name="policyVersion">Optional policy version.</param>
 264    /// <param name="policyHash">Optional policy hash.</param>
 265    /// <returns>A warning governance decision.</returns>
 266    public static GovernanceDecision Warning(
 267        OperationReason reason,
 268        string? correlationId = null,
 269        string? traceId = null,
 270        string? policyVersion = null,
 271        string? policyHash = null)
 272    {
 207273        ArgumentNullException.ThrowIfNull(reason);
 274
 205275        return new GovernanceDecision(
 205276            GovernanceDecisionOutcome.Warning,
 205277            CreateReadOnlyReason(reason),
 205278            correlationId,
 205279            traceId,
 205280            policyVersion,
 205281            policyHash);
 282    }
 283
 284    /// <summary>
 285    /// Creates a warning governance decision.
 286    /// </summary>
 287    /// <param name="reasons">The reasons associated with the warning decision.</param>
 288    /// <param name="correlationId">Optional correlation identifier.</param>
 289    /// <param name="traceId">Optional trace identifier.</param>
 290    /// <param name="policyVersion">Optional policy version.</param>
 291    /// <param name="policyHash">Optional policy hash.</param>
 292    /// <returns>A warning governance decision.</returns>
 293    public static GovernanceDecision Warning(
 294        IEnumerable<OperationReason> reasons,
 295        string? correlationId = null,
 296        string? traceId = null,
 297        string? policyVersion = null,
 298        string? policyHash = null)
 299    {
 36300        return new GovernanceDecision(
 36301            GovernanceDecisionOutcome.Warning,
 36302            NormalizeReasons(reasons, DefaultWarningCode, DefaultWarningMessage),
 36303            correlationId,
 36304            traceId,
 36305            policyVersion,
 36306            policyHash);
 307    }
 308
 309    /// <summary>
 310    /// Creates a deferred governance decision.
 311    /// </summary>
 312    /// <param name="code">The machine-readable reason code.</param>
 313    /// <param name="message">The human-readable reason message.</param>
 314    /// <param name="correlationId">Optional correlation identifier.</param>
 315    /// <param name="traceId">Optional trace identifier.</param>
 316    /// <param name="policyVersion">Optional policy version.</param>
 317    /// <param name="policyHash">Optional policy hash.</param>
 318    /// <returns>A deferred governance decision.</returns>
 319    public static GovernanceDecision Defer(
 320        string code,
 321        string message,
 322        string? correlationId = null,
 323        string? traceId = null,
 324        string? policyVersion = null,
 325        string? policyHash = null)
 326    {
 41327        return new GovernanceDecision(
 41328            GovernanceDecisionOutcome.Deferred,
 41329            CreateReadOnlyReason(OperationReason.Create(code, message)),
 41330            correlationId,
 41331            traceId,
 41332            policyVersion,
 41333            policyHash);
 334    }
 335
 336    /// <summary>
 337    /// Creates an acknowledgment-required governance decision.
 338    /// </summary>
 339    /// <param name="code">The machine-readable reason code.</param>
 340    /// <param name="message">The human-readable reason message.</param>
 341    /// <param name="correlationId">Optional correlation identifier.</param>
 342    /// <param name="traceId">Optional trace identifier.</param>
 343    /// <param name="policyVersion">Optional policy version.</param>
 344    /// <param name="policyHash">Optional policy hash.</param>
 345    /// <returns>An acknowledgment-required governance decision.</returns>
 346    public static GovernanceDecision RequireAcknowledgment(
 347        string code,
 348        string message,
 349        string? correlationId = null,
 350        string? traceId = null,
 351        string? policyVersion = null,
 352        string? policyHash = null)
 353    {
 60354        return new GovernanceDecision(
 60355            GovernanceDecisionOutcome.AcknowledgmentRequired,
 60356            CreateReadOnlyReason(OperationReason.Create(code, message)),
 60357            correlationId,
 60358            traceId,
 60359            policyVersion,
 60360            policyHash);
 361    }
 362
 363    /// <summary>
 364    /// Creates an escalation-recommended governance decision.
 365    /// </summary>
 366    /// <param name="code">The machine-readable reason code.</param>
 367    /// <param name="message">The human-readable reason message.</param>
 368    /// <param name="correlationId">Optional correlation identifier.</param>
 369    /// <param name="traceId">Optional trace identifier.</param>
 370    /// <param name="policyVersion">Optional policy version.</param>
 371    /// <param name="policyHash">Optional policy hash.</param>
 372    /// <returns>An escalation-recommended governance decision.</returns>
 373    public static GovernanceDecision Escalate(
 374        string code,
 375        string message,
 376        string? correlationId = null,
 377        string? traceId = null,
 378        string? policyVersion = null,
 379        string? policyHash = null)
 380    {
 29381        return new GovernanceDecision(
 29382            GovernanceDecisionOutcome.EscalationRecommended,
 29383            CreateReadOnlyReason(OperationReason.Create(code, message)),
 29384            correlationId,
 29385            traceId,
 29386            policyVersion,
 29387            policyHash);
 388    }
 389
 390    private static string? NormalizeOptional(string? value)
 391    {
 3268392        return string.IsNullOrWhiteSpace(value)
 3268393            ? null
 3268394            : value.Trim();
 395    }
 396
 397    private static string? NormalizeTelemetryIdentifier(string? value, int maxLength)
 398    {
 1634399        string? normalized = NormalizeOptional(value);
 400
 1634401        return normalized is null || normalized.Length <= maxLength
 1634402            ? normalized
 1634403            : normalized[..maxLength];
 404    }
 405
 406    private static ReadOnlyCollection<OperationReason> NormalizeReasons(
 407        IEnumerable<OperationReason>? reasons,
 408        string fallbackCode,
 409        string fallbackMessage)
 410    {
 164411        if (reasons is null)
 412        {
 4413            return CreateFallbackReason(fallbackCode, fallbackMessage);
 414        }
 415
 160416        if (reasons is ICollection<OperationReason> collection)
 417        {
 154418            return NormalizeReasonCollection(collection, fallbackCode, fallbackMessage);
 419        }
 420
 6421        OperationReason? firstReason = null;
 6422        List<OperationReason>? normalizedReasons = null;
 423
 28424        foreach (OperationReason? reason in reasons)
 425        {
 8426            if (reason is null)
 427            {
 428                continue;
 429            }
 430
 8431            if (firstReason is null)
 432            {
 4433                firstReason = reason;
 4434                continue;
 435            }
 436
 4437            normalizedReasons ??= [firstReason];
 4438            normalizedReasons.Add(reason);
 439        }
 440
 6441        return firstReason is null
 6442            ? CreateFallbackReason(fallbackCode, fallbackMessage)
 6443            : normalizedReasons is null
 6444            ? CreateReadOnlyReason(firstReason)
 6445            : normalizedReasons.AsReadOnly();
 446    }
 447
 448    private static ReadOnlyCollection<OperationReason> NormalizeReasonCollection(
 449        ICollection<OperationReason> reasons,
 450        string fallbackCode,
 451        string fallbackMessage)
 452    {
 154453        if (reasons.Count == 0)
 454        {
 6455            return CreateFallbackReason(fallbackCode, fallbackMessage);
 456        }
 457
 148458        var normalizedReasons = new OperationReason[reasons.Count];
 148459        int normalizedCount = 0;
 460
 740461        foreach (OperationReason? reason in reasons)
 462        {
 222463            if (reason is not null)
 464            {
 208465                normalizedReasons[normalizedCount] = reason;
 208466                normalizedCount++;
 467            }
 468        }
 469
 148470        if (normalizedCount == 0)
 471        {
 6472            return CreateFallbackReason(fallbackCode, fallbackMessage);
 473        }
 474
 142475        if (normalizedCount == normalizedReasons.Length)
 476        {
 136477            return Array.AsReadOnly(normalizedReasons);
 478        }
 479
 6480        var filteredReasons = new OperationReason[normalizedCount];
 6481        Array.Copy(normalizedReasons, filteredReasons, normalizedCount);
 482
 6483        return Array.AsReadOnly(filteredReasons);
 484    }
 485
 486    private static ReadOnlyCollection<OperationReason> CreateFallbackReason(
 487        string fallbackCode,
 488        string fallbackMessage)
 489    {
 18490        return CreateReadOnlyReason(OperationReason.Create(fallbackCode, fallbackMessage));
 491    }
 492
 493    private static ReadOnlyCollection<OperationReason> CreateReadOnlyReason(OperationReason reason)
 494    {
 458495        return Array.AsReadOnly([reason]);
 496    }
 497
 498    private static ReadOnlyCollection<string> CreateReasonCodes(IReadOnlyList<OperationReason> reasons)
 499    {
 817500        if (reasons.Count == 0)
 501        {
 213502            return EmptyReasonCodes;
 503        }
 504
 604505        string[] reasonCodes = new string[reasons.Count];
 506
 2556507        for (int index = 0; index < reasons.Count; index++)
 508        {
 674509            reasonCodes[index] = reasons[index].Code;
 510        }
 511
 604512        return Array.AsReadOnly(reasonCodes);
 513    }
 514}

Methods/Properties

.cctor()
.ctor(AsiBackbone.Core.Decisions.GovernanceDecisionOutcome,System.Collections.Generic.IReadOnlyList`1<AsiBackbone.Core.Results.OperationReason>,System.String,System.String,System.String,System.String)
get_Outcome()
get_Reasons()
get_ReasonCodes()
get_CorrelationId()
get_TraceId()
get_PolicyVersion()
get_PolicyHash()
get_CanProceed()
get_IsAllowed()
get_IsWarning()
get_IsDenied()
get_IsDeferred()
get_RequiresAcknowledgment()
get_EscalationRecommended()
get_HasReasons()
Allow(System.String,System.String,System.String,System.String)
Deny(System.String,System.String,System.String,System.String,System.String,System.String)
Deny(AsiBackbone.Core.Results.OperationReason,System.String,System.String,System.String,System.String)
Deny(System.Collections.Generic.IEnumerable`1<AsiBackbone.Core.Results.OperationReason>,System.String,System.String,System.String,System.String)
Warning(System.String,System.String,System.String,System.String,System.String,System.String)
Warning(AsiBackbone.Core.Results.OperationReason,System.String,System.String,System.String,System.String)
Warning(System.Collections.Generic.IEnumerable`1<AsiBackbone.Core.Results.OperationReason>,System.String,System.String,System.String,System.String)
Defer(System.String,System.String,System.String,System.String,System.String,System.String)
RequireAcknowledgment(System.String,System.String,System.String,System.String,System.String,System.String)
Escalate(System.String,System.String,System.String,System.String,System.String,System.String)
NormalizeOptional(System.String)
NormalizeTelemetryIdentifier(System.String,System.Int32)
NormalizeReasons(System.Collections.Generic.IEnumerable`1<AsiBackbone.Core.Results.OperationReason>,System.String,System.String)
NormalizeReasonCollection(System.Collections.Generic.ICollection`1<AsiBackbone.Core.Results.OperationReason>,System.String,System.String)
CreateFallbackReason(System.String,System.String)
CreateReadOnlyReason(AsiBackbone.Core.Results.OperationReason)
CreateReasonCodes(System.Collections.Generic.IReadOnlyList`1<AsiBackbone.Core.Results.OperationReason>)