using System; using System.Collections.Immutable; using System.Linq; namespace StellaOps.Policy; public sealed record PolicyDiagnosticsReport( string Version, int RuleCount, int ErrorCount, int WarningCount, DateTimeOffset GeneratedAt, ImmutableArray Issues, ImmutableArray Recommendations); public static class PolicyDiagnostics { public static PolicyDiagnosticsReport Create(PolicyBindingResult bindingResult, TimeProvider? timeProvider = null) { if (bindingResult is null) { throw new ArgumentNullException(nameof(bindingResult)); } var time = (timeProvider ?? TimeProvider.System).GetUtcNow(); var errorCount = bindingResult.Issues.Count(static issue => issue.Severity == PolicyIssueSeverity.Error); var warningCount = bindingResult.Issues.Count(static issue => issue.Severity == PolicyIssueSeverity.Warning); var recommendations = BuildRecommendations(bindingResult.Document, errorCount, warningCount); return new PolicyDiagnosticsReport( bindingResult.Document.Version, bindingResult.Document.Rules.Length, errorCount, warningCount, time, bindingResult.Issues, recommendations); } private static ImmutableArray BuildRecommendations(PolicyDocument document, int errorCount, int warningCount) { var messages = ImmutableArray.CreateBuilder(); if (errorCount > 0) { messages.Add("Resolve policy errors before promoting the revision; fallback rules may be applied while errors remain."); } if (warningCount > 0) { messages.Add("Review policy warnings and ensure intentional overrides are documented."); } if (document.Rules.Length == 0) { messages.Add("Add at least one policy rule to enforce gating logic."); } var quietRules = document.Rules .Where(static rule => rule.Action.Quiet) .Select(static rule => rule.Name) .ToArray(); if (quietRules.Length > 0) { messages.Add($"Quiet rules detected ({string.Join(", ", quietRules)}); verify scoring behaviour aligns with expectations."); } if (messages.Count == 0) { messages.Add("Policy validated successfully; no additional action required."); } return messages.ToImmutable(); } }