namespace StellaOps.Metrics.Kpi; /// /// Quality KPIs for explainable triage. /// public sealed record TriageQualityKpis { /// /// Reporting period start. /// public required DateTimeOffset PeriodStart { get; init; } /// /// Reporting period end. /// public required DateTimeOffset PeriodEnd { get; init; } /// /// Tenant ID (null for global). /// public string? TenantId { get; init; } /// /// Reachability KPIs. /// public required ReachabilityKpis Reachability { get; init; } /// /// Runtime KPIs. /// public required RuntimeKpis Runtime { get; init; } /// /// Explainability KPIs. /// public required ExplainabilityKpis Explainability { get; init; } /// /// Replay/Determinism KPIs. /// public required ReplayKpis Replay { get; init; } /// /// Unknown budget KPIs. /// public required UnknownBudgetKpis Unknowns { get; init; } /// /// Operational KPIs. /// public required OperationalKpis Operational { get; init; } } public sealed record ReachabilityKpis { /// /// Total findings analyzed. /// public required int TotalFindings { get; init; } /// /// Findings with non-UNKNOWN reachability. /// public required int WithKnownReachability { get; init; } /// /// Percentage with known reachability. /// public decimal PercentKnown => TotalFindings > 0 ? (decimal)WithKnownReachability / TotalFindings * 100 : 0; /// /// Breakdown by reachability state. /// public required IReadOnlyDictionary ByState { get; init; } /// /// Findings confirmed unreachable. /// public int ConfirmedUnreachable => ByState.GetValueOrDefault("ConfirmedUnreachable", 0); /// /// Noise reduction (unreachable / total). /// public decimal NoiseReductionPercent => TotalFindings > 0 ? (decimal)ConfirmedUnreachable / TotalFindings * 100 : 0; } public sealed record RuntimeKpis { /// /// Total findings in environments with sensors. /// public required int TotalWithSensorDeployed { get; init; } /// /// Findings with runtime observations. /// public required int WithRuntimeCorroboration { get; init; } /// /// Coverage percentage. /// public decimal CoveragePercent => TotalWithSensorDeployed > 0 ? (decimal)WithRuntimeCorroboration / TotalWithSensorDeployed * 100 : 0; /// /// Breakdown by posture. /// public required IReadOnlyDictionary ByPosture { get; init; } } public sealed record ExplainabilityKpis { /// /// Total verdicts generated. /// public required int TotalVerdicts { get; init; } /// /// Verdicts with reason steps. /// public required int WithReasonSteps { get; init; } /// /// Verdicts with at least one proof pointer. /// public required int WithProofPointer { get; init; } /// /// Verdicts that are "complete" (both reason steps AND proof pointer). /// public required int FullyExplainable { get; init; } /// /// Explainability completeness percentage. /// public decimal CompletenessPercent => TotalVerdicts > 0 ? (decimal)FullyExplainable / TotalVerdicts * 100 : 0; } public sealed record ReplayKpis { /// /// Total replay attempts. /// public required int TotalAttempts { get; init; } /// /// Successful replays (identical verdict). /// public required int Successful { get; init; } /// /// Replay success rate. /// public decimal SuccessRate => TotalAttempts > 0 ? (decimal)Successful / TotalAttempts * 100 : 0; /// /// Common failure reasons. /// public required IReadOnlyDictionary FailureReasons { get; init; } } public sealed record UnknownBudgetKpis { /// /// Total environments tracked. /// public required int TotalEnvironments { get; init; } /// /// Budget breaches by environment. /// public required IReadOnlyDictionary BreachesByEnvironment { get; init; } /// /// Total overrides/exceptions granted. /// public required int OverridesGranted { get; init; } /// /// Average override age (days). /// public decimal AvgOverrideAgeDays { get; init; } } public sealed record OperationalKpis { /// /// Median time to first verdict (seconds). /// public required double MedianTimeToVerdictSeconds { get; init; } /// /// Cache hit rate for graphs/proofs. /// public required decimal CacheHitRate { get; init; } /// /// Average evidence size per scan (bytes). /// public required long AvgEvidenceSizeBytes { get; init; } /// /// 95th percentile verdict time (seconds). /// public required double P95VerdictTimeSeconds { get; init; } }