tests fixes and sprints work
This commit is contained in:
@@ -0,0 +1,605 @@
|
||||
// -----------------------------------------------------------------------------
|
||||
// IKpiRepository.cs
|
||||
// Sprint: SPRINT_20260121_034_BinaryIndex_golden_corpus_foundation
|
||||
// Task: GCF-004 - Define KPI tracking schema and infrastructure
|
||||
// Description: Repository interface for KPI tracking and baseline management
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
using System.Collections.Immutable;
|
||||
|
||||
namespace StellaOps.BinaryIndex.GroundTruth.Abstractions;
|
||||
|
||||
/// <summary>
|
||||
/// Repository for recording and querying validation KPIs.
|
||||
/// </summary>
|
||||
public interface IKpiRepository
|
||||
{
|
||||
/// <summary>
|
||||
/// Records KPIs from a validation run.
|
||||
/// </summary>
|
||||
/// <param name="kpis">The KPIs to record.</param>
|
||||
/// <param name="ct">Cancellation token.</param>
|
||||
/// <returns>The recorded KPI entry ID.</returns>
|
||||
Task<Guid> RecordAsync(ValidationKpis kpis, CancellationToken ct = default);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the active baseline for a tenant and corpus version.
|
||||
/// </summary>
|
||||
/// <param name="tenantId">The tenant ID.</param>
|
||||
/// <param name="corpusVersion">The corpus version.</param>
|
||||
/// <param name="ct">Cancellation token.</param>
|
||||
/// <returns>The active baseline, or null if none exists.</returns>
|
||||
Task<KpiBaseline?> GetBaselineAsync(
|
||||
string tenantId,
|
||||
string corpusVersion,
|
||||
CancellationToken ct = default);
|
||||
|
||||
/// <summary>
|
||||
/// Sets a new baseline from a validation run.
|
||||
/// </summary>
|
||||
/// <param name="runId">The validation run ID to use as baseline.</param>
|
||||
/// <param name="createdBy">Who is setting the baseline.</param>
|
||||
/// <param name="reason">Reason for setting the baseline.</param>
|
||||
/// <param name="ct">Cancellation token.</param>
|
||||
/// <returns>The created baseline.</returns>
|
||||
Task<KpiBaseline> SetBaselineAsync(
|
||||
Guid runId,
|
||||
string createdBy,
|
||||
string? reason = null,
|
||||
CancellationToken ct = default);
|
||||
|
||||
/// <summary>
|
||||
/// Compares a validation run against the active baseline.
|
||||
/// </summary>
|
||||
/// <param name="runId">The validation run ID to compare.</param>
|
||||
/// <param name="ct">Cancellation token.</param>
|
||||
/// <returns>The regression check result.</returns>
|
||||
Task<RegressionCheckResult> CompareAsync(
|
||||
Guid runId,
|
||||
CancellationToken ct = default);
|
||||
|
||||
/// <summary>
|
||||
/// Gets KPIs for a specific validation run.
|
||||
/// </summary>
|
||||
/// <param name="runId">The run ID.</param>
|
||||
/// <param name="ct">Cancellation token.</param>
|
||||
/// <returns>The KPIs, or null if not found.</returns>
|
||||
Task<ValidationKpis?> GetByRunIdAsync(Guid runId, CancellationToken ct = default);
|
||||
|
||||
/// <summary>
|
||||
/// Gets recent validation runs for a tenant.
|
||||
/// </summary>
|
||||
/// <param name="tenantId">The tenant ID.</param>
|
||||
/// <param name="limit">Maximum number of runs to return.</param>
|
||||
/// <param name="ct">Cancellation token.</param>
|
||||
/// <returns>Recent validation runs.</returns>
|
||||
Task<ImmutableArray<ValidationKpis>> GetRecentAsync(
|
||||
string tenantId,
|
||||
int limit = 10,
|
||||
CancellationToken ct = default);
|
||||
|
||||
/// <summary>
|
||||
/// Gets KPI trends over time.
|
||||
/// </summary>
|
||||
/// <param name="tenantId">The tenant ID.</param>
|
||||
/// <param name="corpusVersion">Optional corpus version filter.</param>
|
||||
/// <param name="since">Start date for trend data.</param>
|
||||
/// <param name="ct">Cancellation token.</param>
|
||||
/// <returns>KPI trend data points.</returns>
|
||||
Task<ImmutableArray<KpiTrendPoint>> GetTrendAsync(
|
||||
string tenantId,
|
||||
string? corpusVersion = null,
|
||||
DateTimeOffset? since = null,
|
||||
CancellationToken ct = default);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Recorded validation KPIs.
|
||||
/// </summary>
|
||||
public sealed record ValidationKpis
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the unique run ID.
|
||||
/// </summary>
|
||||
public required Guid RunId { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the tenant ID.
|
||||
/// </summary>
|
||||
public required string TenantId { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the corpus version.
|
||||
/// </summary>
|
||||
public required string CorpusVersion { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the scanner version.
|
||||
/// </summary>
|
||||
public string ScannerVersion { get; init; } = "0.0.0";
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of pairs validated.
|
||||
/// </summary>
|
||||
public required int PairCount { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the mean function match rate (0-100).
|
||||
/// </summary>
|
||||
public double? FunctionMatchRateMean { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the minimum function match rate (0-100).
|
||||
/// </summary>
|
||||
public double? FunctionMatchRateMin { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the maximum function match rate (0-100).
|
||||
/// </summary>
|
||||
public double? FunctionMatchRateMax { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the mean false-negative rate (0-100).
|
||||
/// </summary>
|
||||
public double? FalseNegativeRateMean { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the maximum false-negative rate (0-100).
|
||||
/// </summary>
|
||||
public double? FalseNegativeRateMax { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the count of pairs with 3/3 SBOM hash stability.
|
||||
/// </summary>
|
||||
public int SbomHashStability3of3Count { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the count of pairs with 2/3 SBOM hash stability.
|
||||
/// </summary>
|
||||
public int SbomHashStability2of3Count { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the count of pairs with 1/3 SBOM hash stability.
|
||||
/// </summary>
|
||||
public int SbomHashStability1of3Count { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the count of reconstruction-equivalent pairs.
|
||||
/// </summary>
|
||||
public int ReconstructionEquivCount { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the total pairs tested for reconstruction.
|
||||
/// </summary>
|
||||
public int ReconstructionTotalCount { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the median verify time in milliseconds.
|
||||
/// </summary>
|
||||
public int? VerifyTimeMedianMs { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the p95 verify time in milliseconds.
|
||||
/// </summary>
|
||||
public int? VerifyTimeP95Ms { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the p99 verify time in milliseconds.
|
||||
/// </summary>
|
||||
public int? VerifyTimeP99Ms { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the precision (0-1).
|
||||
/// </summary>
|
||||
public double? Precision { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the recall (0-1).
|
||||
/// </summary>
|
||||
public double? Recall { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the F1 score (0-1).
|
||||
/// </summary>
|
||||
public double? F1Score { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the deterministic replay rate (0-1).
|
||||
/// </summary>
|
||||
public double? DeterministicReplayRate { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the total functions in post-patch binaries.
|
||||
/// </summary>
|
||||
public int TotalFunctionsPost { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the matched functions count.
|
||||
/// </summary>
|
||||
public int MatchedFunctions { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the total true patched functions.
|
||||
/// </summary>
|
||||
public int TotalTruePatched { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the missed patched functions count.
|
||||
/// </summary>
|
||||
public int MissedPatched { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets when the run was computed.
|
||||
/// </summary>
|
||||
public DateTimeOffset ComputedAt { get; init; } = DateTimeOffset.UtcNow;
|
||||
|
||||
/// <summary>
|
||||
/// Gets when the run started.
|
||||
/// </summary>
|
||||
public DateTimeOffset? StartedAt { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets when the run completed.
|
||||
/// </summary>
|
||||
public DateTimeOffset? CompletedAt { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets per-pair KPI results.
|
||||
/// </summary>
|
||||
public ImmutableArray<PairKpis>? PairResults { get; init; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Per-pair KPI results.
|
||||
/// </summary>
|
||||
public sealed record PairKpis
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the pair ID.
|
||||
/// </summary>
|
||||
public required string PairId { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the CVE ID.
|
||||
/// </summary>
|
||||
public required string CveId { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the package name.
|
||||
/// </summary>
|
||||
public required string PackageName { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the function match rate (0-100).
|
||||
/// </summary>
|
||||
public double? FunctionMatchRate { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the false-negative rate (0-100).
|
||||
/// </summary>
|
||||
public double? FalseNegativeRate { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the SBOM hash stability (0-3).
|
||||
/// </summary>
|
||||
public int SbomHashStability { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets whether the binary is reconstruction-equivalent.
|
||||
/// </summary>
|
||||
public bool? ReconstructionEquivalent { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the total functions in the post-patch binary.
|
||||
/// </summary>
|
||||
public int TotalFunctionsPost { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the matched functions count.
|
||||
/// </summary>
|
||||
public int MatchedFunctions { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the total known patched functions.
|
||||
/// </summary>
|
||||
public int TotalPatchedFunctions { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the patched functions detected.
|
||||
/// </summary>
|
||||
public int PatchedFunctionsDetected { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the verify time in milliseconds.
|
||||
/// </summary>
|
||||
public int? VerifyTimeMs { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets whether validation succeeded.
|
||||
/// </summary>
|
||||
public bool Success { get; init; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the error message if validation failed.
|
||||
/// </summary>
|
||||
public string? ErrorMessage { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the SBOM hash.
|
||||
/// </summary>
|
||||
public string? SbomHash { get; init; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// KPI baseline for regression detection.
|
||||
/// </summary>
|
||||
public sealed record KpiBaseline
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the baseline ID.
|
||||
/// </summary>
|
||||
public required Guid BaselineId { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the tenant ID.
|
||||
/// </summary>
|
||||
public required string TenantId { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the corpus version.
|
||||
/// </summary>
|
||||
public required string CorpusVersion { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the baseline precision (0-1).
|
||||
/// </summary>
|
||||
public required double PrecisionBaseline { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the baseline recall (0-1).
|
||||
/// </summary>
|
||||
public required double RecallBaseline { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the baseline F1 score (0-1).
|
||||
/// </summary>
|
||||
public required double F1Baseline { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the baseline false-negative rate (0-1).
|
||||
/// </summary>
|
||||
public required double FnRateBaseline { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the baseline p95 verify time in milliseconds.
|
||||
/// </summary>
|
||||
public required int VerifyP95BaselineMs { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the precision warning delta (percentage points).
|
||||
/// </summary>
|
||||
public double PrecisionWarnDelta { get; init; } = 0.005;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the precision fail delta (percentage points).
|
||||
/// </summary>
|
||||
public double PrecisionFailDelta { get; init; } = 0.010;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the recall warning delta.
|
||||
/// </summary>
|
||||
public double RecallWarnDelta { get; init; } = 0.005;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the recall fail delta.
|
||||
/// </summary>
|
||||
public double RecallFailDelta { get; init; } = 0.010;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the false-negative rate warning delta.
|
||||
/// </summary>
|
||||
public double FnRateWarnDelta { get; init; } = 0.005;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the false-negative rate fail delta.
|
||||
/// </summary>
|
||||
public double FnRateFailDelta { get; init; } = 0.010;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the verify time warning delta percentage.
|
||||
/// </summary>
|
||||
public double VerifyWarnDeltaPct { get; init; } = 10.0;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the verify time fail delta percentage.
|
||||
/// </summary>
|
||||
public double VerifyFailDeltaPct { get; init; } = 20.0;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the source validation run ID.
|
||||
/// </summary>
|
||||
public Guid? SourceRunId { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets when the baseline was created.
|
||||
/// </summary>
|
||||
public DateTimeOffset CreatedAt { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets who created the baseline.
|
||||
/// </summary>
|
||||
public required string CreatedBy { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the reason for creating the baseline.
|
||||
/// </summary>
|
||||
public string? Reason { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets whether this is the active baseline.
|
||||
/// </summary>
|
||||
public bool IsActive { get; init; } = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Result of a regression check.
|
||||
/// </summary>
|
||||
public sealed record RegressionCheckResult
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the check ID.
|
||||
/// </summary>
|
||||
public required Guid CheckId { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the validation run ID.
|
||||
/// </summary>
|
||||
public required Guid RunId { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the baseline ID.
|
||||
/// </summary>
|
||||
public required Guid BaselineId { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the precision delta (current - baseline).
|
||||
/// </summary>
|
||||
public double? PrecisionDelta { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the recall delta.
|
||||
/// </summary>
|
||||
public double? RecallDelta { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the F1 delta.
|
||||
/// </summary>
|
||||
public double? F1Delta { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the false-negative rate delta.
|
||||
/// </summary>
|
||||
public double? FnRateDelta { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the verify p95 delta percentage.
|
||||
/// </summary>
|
||||
public double? VerifyP95DeltaPct { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the overall status.
|
||||
/// </summary>
|
||||
public required RegressionStatus OverallStatus { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the precision status.
|
||||
/// </summary>
|
||||
public required RegressionStatus PrecisionStatus { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the recall status.
|
||||
/// </summary>
|
||||
public required RegressionStatus RecallStatus { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the false-negative rate status.
|
||||
/// </summary>
|
||||
public required RegressionStatus FnRateStatus { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the verify time status.
|
||||
/// </summary>
|
||||
public required RegressionStatus VerifyTimeStatus { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the determinism status.
|
||||
/// </summary>
|
||||
public required RegressionStatus DeterminismStatus { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets when the check was performed.
|
||||
/// </summary>
|
||||
public DateTimeOffset CheckedAt { get; init; } = DateTimeOffset.UtcNow;
|
||||
|
||||
/// <summary>
|
||||
/// Gets any notes about the check.
|
||||
/// </summary>
|
||||
public string? Notes { get; init; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Status of a regression check metric.
|
||||
/// </summary>
|
||||
public enum RegressionStatus
|
||||
{
|
||||
/// <summary>
|
||||
/// Metric passed threshold checks.
|
||||
/// </summary>
|
||||
Pass,
|
||||
|
||||
/// <summary>
|
||||
/// Metric is within warning threshold.
|
||||
/// </summary>
|
||||
Warn,
|
||||
|
||||
/// <summary>
|
||||
/// Metric failed threshold check.
|
||||
/// </summary>
|
||||
Fail,
|
||||
|
||||
/// <summary>
|
||||
/// Metric improved over baseline.
|
||||
/// </summary>
|
||||
Improved
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// KPI trend data point.
|
||||
/// </summary>
|
||||
public sealed record KpiTrendPoint
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the run ID.
|
||||
/// </summary>
|
||||
public required Guid RunId { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the timestamp.
|
||||
/// </summary>
|
||||
public required DateTimeOffset Timestamp { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the corpus version.
|
||||
/// </summary>
|
||||
public required string CorpusVersion { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the precision.
|
||||
/// </summary>
|
||||
public double? Precision { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the recall.
|
||||
/// </summary>
|
||||
public double? Recall { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the F1 score.
|
||||
/// </summary>
|
||||
public double? F1Score { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the false-negative rate.
|
||||
/// </summary>
|
||||
public double? FalseNegativeRate { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the verify time p95 in milliseconds.
|
||||
/// </summary>
|
||||
public int? VerifyTimeP95Ms { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the deterministic replay rate.
|
||||
/// </summary>
|
||||
public double? DeterministicReplayRate { get; init; }
|
||||
}
|
||||
Reference in New Issue
Block a user