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; }
}