This commit is contained in:
StellaOps Bot
2025-12-14 23:20:14 +02:00
parent 3411e825cd
commit b058dbe031
356 changed files with 68310 additions and 1108 deletions

View File

@@ -0,0 +1,40 @@
using Xunit;
namespace StellaOps.Telemetry.Core.Tests;
public sealed class ProofCoverageMetricsTests
{
[Theory]
[InlineData(0, 0, 1.0)]
[InlineData(0, 10, 0.0)]
[InlineData(5, 10, 0.5)]
[InlineData(10, 10, 1.0)]
public void ComputeCoverageRatio_HandlesZeroDenominator(int withProof, int total, double expected)
{
var ratio = ProofCoverageMetrics.ComputeCoverageRatio(withProof, total);
Assert.Equal(expected, ratio, precision: 10);
}
[Fact]
public void RecordScanCoverage_StoresLatestValues()
{
using var metrics = new ProofCoverageMetrics();
metrics.RecordScanCoverage(
tenantId: "tenant-1",
surfaceId: "surface-1",
findingsWithReceipts: 5,
totalFindings: 10,
vexWithReceipts: 0,
totalVex: 0,
reachableWithProofs: 2,
totalReachable: 4);
Assert.True(metrics.TryGetLastCoverage("tenant-1", "surface-1", out var coverage));
Assert.Equal(0.5, coverage.All, precision: 10);
Assert.Equal(1.0, coverage.Vex, precision: 10);
Assert.Equal(0.5, coverage.Reachable, precision: 10);
}
}

View File

@@ -0,0 +1,167 @@
using System.Collections.Concurrent;
using System.Diagnostics.Metrics;
namespace StellaOps.Telemetry.Core;
/// <summary>
/// Metrics for proof coverage tracking.
/// Measures ratio of findings / VEX items / reachable findings with valid cryptographic receipts.
/// </summary>
public sealed class ProofCoverageMetrics : IDisposable
{
public const string MeterName = "StellaOps.ProofCoverage";
private readonly Meter _meter;
private readonly ConcurrentDictionary<(string TenantId, string SurfaceId), double> _allCoverage = new();
private readonly ConcurrentDictionary<(string TenantId, string SurfaceId), double> _vexCoverage = new();
private readonly ConcurrentDictionary<(string TenantId, string SurfaceId), double> _reachableCoverage = new();
private readonly ObservableGauge<double> _proofCoverageAll;
private readonly ObservableGauge<double> _proofCoverageVex;
private readonly ObservableGauge<double> _proofCoverageReachable;
private readonly Counter<long> _findingsWithProof;
private readonly Counter<long> _findingsWithoutProof;
public ProofCoverageMetrics(string version = "1.0.0")
{
_meter = new Meter(MeterName, version);
_proofCoverageAll = _meter.CreateObservableGauge(
name: "stellaops_proof_coverage_all",
observeValues: ObserveAllCoverage,
unit: "1",
description: "Ratio of findings with valid receipts to total findings.");
_proofCoverageVex = _meter.CreateObservableGauge(
name: "stellaops_proof_coverage_vex",
observeValues: ObserveVexCoverage,
unit: "1",
description: "Ratio of VEX items with valid receipts to total VEX items.");
_proofCoverageReachable = _meter.CreateObservableGauge(
name: "stellaops_proof_coverage_reachable",
observeValues: ObserveReachableCoverage,
unit: "1",
description: "Ratio of reachable findings with proofs to total reachable findings.");
_findingsWithProof = _meter.CreateCounter<long>(
name: "stellaops_findings_with_proof_total",
unit: "{finding}",
description: "Total findings with valid cryptographic proofs.");
_findingsWithoutProof = _meter.CreateCounter<long>(
name: "stellaops_findings_without_proof_total",
unit: "{finding}",
description: "Total findings without valid cryptographic proofs.");
}
/// <summary>
/// Records proof coverage for a completed scan.
/// </summary>
public void RecordScanCoverage(
string tenantId,
string surfaceId,
int findingsWithReceipts,
int totalFindings,
int vexWithReceipts,
int totalVex,
int reachableWithProofs,
int totalReachable)
{
tenantId = NormalizeLabel(tenantId);
surfaceId = NormalizeLabel(surfaceId);
var key = (tenantId, surfaceId);
var allCoverage = ComputeCoverageRatio(findingsWithReceipts, totalFindings);
var vexCoverage = ComputeCoverageRatio(vexWithReceipts, totalVex);
var reachableCoverage = ComputeCoverageRatio(reachableWithProofs, totalReachable);
_allCoverage[key] = allCoverage;
_vexCoverage[key] = vexCoverage;
_reachableCoverage[key] = reachableCoverage;
_findingsWithProof.Add(Math.Max(0, findingsWithReceipts),
new KeyValuePair<string, object?>("tenant_id", tenantId),
new KeyValuePair<string, object?>("proof_type", "receipt"));
_findingsWithoutProof.Add(Math.Max(0, totalFindings - findingsWithReceipts),
new KeyValuePair<string, object?>("tenant_id", tenantId),
new KeyValuePair<string, object?>("reason", "missing_receipt"));
_findingsWithProof.Add(Math.Max(0, vexWithReceipts),
new KeyValuePair<string, object?>("tenant_id", tenantId),
new KeyValuePair<string, object?>("proof_type", "vex_receipt"));
_findingsWithoutProof.Add(Math.Max(0, totalVex - vexWithReceipts),
new KeyValuePair<string, object?>("tenant_id", tenantId),
new KeyValuePair<string, object?>("reason", "missing_vex_receipt"));
_findingsWithProof.Add(Math.Max(0, reachableWithProofs),
new KeyValuePair<string, object?>("tenant_id", tenantId),
new KeyValuePair<string, object?>("proof_type", "reachability_proof"));
_findingsWithoutProof.Add(Math.Max(0, totalReachable - reachableWithProofs),
new KeyValuePair<string, object?>("tenant_id", tenantId),
new KeyValuePair<string, object?>("reason", "missing_reachability_proof"));
}
public bool TryGetLastCoverage(
string tenantId,
string surfaceId,
out (double All, double Vex, double Reachable) coverage)
{
coverage = default;
tenantId = NormalizeLabel(tenantId);
surfaceId = NormalizeLabel(surfaceId);
var key = (tenantId, surfaceId);
if (!_allCoverage.TryGetValue(key, out var all))
{
return false;
}
_vexCoverage.TryGetValue(key, out var vex);
_reachableCoverage.TryGetValue(key, out var reachable);
coverage = (all, vex, reachable);
return true;
}
public static double ComputeCoverageRatio(int withProof, int total)
{
if (total <= 0)
{
return 1.0;
}
return (double)withProof / total;
}
private IEnumerable<Measurement<double>> ObserveAllCoverage() => Observe(_allCoverage);
private IEnumerable<Measurement<double>> ObserveVexCoverage() => Observe(_vexCoverage);
private IEnumerable<Measurement<double>> ObserveReachableCoverage() => Observe(_reachableCoverage);
private static IEnumerable<Measurement<double>> Observe(
ConcurrentDictionary<(string TenantId, string SurfaceId), double> values)
{
foreach (var (key, value) in values)
{
yield return new Measurement<double>(
value,
new KeyValuePair<string, object?>("tenant_id", key.TenantId),
new KeyValuePair<string, object?>("surface_id", key.SurfaceId));
}
}
private static string NormalizeLabel(string? value)
=> string.IsNullOrWhiteSpace(value) ? "unknown" : value.Trim();
public void Dispose()
{
_meter.Dispose();
}
}

View File

@@ -0,0 +1,8 @@
# Telemetry Core Local Tasks
This file mirrors sprint work for the Telemetry Core module.
| Task ID | Sprint | Status | Notes |
| --- | --- | --- | --- |
| `DET-3401-005` | `docs/implplan/SPRINT_3401_0001_0001_determinism_scoring_foundations.md` | DONE (2025-12-14) | Added `ProofCoverageMetrics` (`System.Diagnostics.Metrics`) in `src/Telemetry/StellaOps.Telemetry.Core/StellaOps.Telemetry.Core/ProofCoverageMetrics.cs` and tests in `src/Telemetry/StellaOps.Telemetry.Core/StellaOps.Telemetry.Core.Tests/ProofCoverageMetricsTests.cs`. |