Files
git.stella-ops.org/src/Policy/__Libraries/StellaOps.Policy/Gates/MinimumConfidenceGate.cs
2026-02-01 21:37:40 +02:00

89 lines
2.6 KiB
C#

using StellaOps.Policy.TrustLattice;
using System.Collections.Immutable;
using VexStatus = StellaOps.Policy.Confidence.Models.VexStatus;
namespace StellaOps.Policy.Gates;
public sealed record MinimumConfidenceGateOptions
{
public bool Enabled { get; init; } = true;
public IReadOnlyDictionary<string, double> Thresholds { get; init; } = new Dictionary<string, double>(StringComparer.OrdinalIgnoreCase)
{
["production"] = 0.75,
["staging"] = 0.60,
["development"] = 0.40,
};
public IReadOnlyCollection<VexStatus> ApplyToStatuses { get; init; } = new[]
{
VexStatus.NotAffected,
VexStatus.Fixed,
};
}
public sealed class MinimumConfidenceGate : IPolicyGate
{
private readonly MinimumConfidenceGateOptions _options;
public MinimumConfidenceGate(MinimumConfidenceGateOptions? options = null)
{
_options = options ?? new MinimumConfidenceGateOptions();
}
public Task<GateResult> EvaluateAsync(MergeResult mergeResult, PolicyGateContext context, CancellationToken ct = default)
{
if (!_options.Enabled)
{
return Task.FromResult(Pass("disabled"));
}
if (mergeResult.Status == VexStatus.Affected)
{
return Task.FromResult(Pass("affected_bypass"));
}
if (!_options.ApplyToStatuses.Contains(mergeResult.Status))
{
return Task.FromResult(Pass("status_not_applicable"));
}
var threshold = GetThreshold(context.Environment);
var passed = mergeResult.Confidence >= threshold;
var details = ImmutableDictionary<string, object>.Empty
.Add("threshold", threshold)
.Add("confidence", mergeResult.Confidence)
.Add("environment", context.Environment);
return Task.FromResult(new GateResult
{
GateName = nameof(MinimumConfidenceGate),
Passed = passed,
Reason = passed ? null : "confidence_below_threshold",
Details = details,
});
}
private double GetThreshold(string environment)
{
if (_options.Thresholds.TryGetValue(environment, out var threshold))
{
return threshold;
}
if (_options.Thresholds.TryGetValue("production", out var prod))
{
return prod;
}
return 0.0;
}
private static GateResult Pass(string reason) => new()
{
GateName = nameof(MinimumConfidenceGate),
Passed = true,
Reason = reason,
Details = ImmutableDictionary<string, object>.Empty,
};
}