namespace StellaOps.Policy.Deltas;
///
/// Verdict for a security state delta.
/// Determines whether a change should be allowed to proceed.
///
public sealed record DeltaVerdict
{
///
/// Unique identifier for this verdict.
///
public required string VerdictId { get; init; }
///
/// Reference to the delta being evaluated.
///
public required string DeltaId { get; init; }
///
/// When this verdict was rendered.
///
public required DateTimeOffset EvaluatedAt { get; init; }
///
/// The verdict outcome.
///
public required DeltaVerdictStatus Status { get; init; }
///
/// Recommended gate level based on delta risk.
///
public DeltaGateLevel RecommendedGate { get; init; }
///
/// Risk points consumed by this change.
///
public int RiskPoints { get; init; }
///
/// Drivers that contributed to the verdict.
///
public IReadOnlyList BlockingDrivers { get; init; } = [];
///
/// Drivers that raised warnings but didn't block.
///
public IReadOnlyList WarningDrivers { get; init; } = [];
///
/// Applied exceptions that allowed blocking drivers.
///
public IReadOnlyList AppliedExceptions { get; init; } = [];
///
/// Human-readable explanation.
///
public string? Explanation { get; init; }
///
/// Recommendations for addressing issues.
///
public IReadOnlyList Recommendations { get; init; } = [];
}
///
/// Possible verdict outcomes for a delta.
///
public enum DeltaVerdictStatus
{
///
/// Delta is safe to proceed.
///
Pass,
///
/// Delta has warnings but can proceed.
///
Warn,
///
/// Delta should not proceed without remediation.
///
Fail,
///
/// Delta is blocked but covered by exceptions.
///
PassWithExceptions
}
///
/// Gate levels aligned with diff-aware release gates.
///
public enum DeltaGateLevel
{
///
/// G0: No-risk (docs, comments only).
///
G0,
///
/// G1: Low risk (unit tests, 1 review).
///
G1,
///
/// G2: Moderate risk (integration tests, code owner, canary).
///
G2,
///
/// G3: High risk (security scan, migration plan, release captain).
///
G3,
///
/// G4: Very high risk (formal review, extended canary, comms plan).
///
G4
}
///
/// Builder for delta verdicts.
///
public sealed class DeltaVerdictBuilder
{
private static readonly IVerdictIdGenerator DefaultIdGenerator = new VerdictIdGenerator();
private readonly IVerdictIdGenerator _idGenerator;
private DeltaVerdictStatus _status = DeltaVerdictStatus.Pass;
private DeltaGateLevel _gate = DeltaGateLevel.G1;
private int _riskPoints;
private readonly List _blockingDrivers = [];
private readonly List _warningDrivers = [];
private readonly List _exceptions = [];
private readonly List _recommendations = [];
private string? _explanation;
///
/// Creates a new with the default ID generator.
///
public DeltaVerdictBuilder() : this(DefaultIdGenerator)
{
}
///
/// Creates a new with a custom ID generator.
///
/// Custom verdict ID generator for testing or specialized scenarios.
public DeltaVerdictBuilder(IVerdictIdGenerator idGenerator)
{
_idGenerator = idGenerator ?? throw new ArgumentNullException(nameof(idGenerator));
}
public DeltaVerdictBuilder WithStatus(DeltaVerdictStatus status)
{
_status = status;
return this;
}
public DeltaVerdictBuilder WithGate(DeltaGateLevel gate)
{
_gate = gate;
return this;
}
public DeltaVerdictBuilder WithRiskPoints(int points)
{
_riskPoints = points;
return this;
}
public DeltaVerdictBuilder AddBlockingDriver(DeltaDriver driver)
{
_blockingDrivers.Add(driver);
_status = DeltaVerdictStatus.Fail;
// Escalate gate based on severity
if (driver.Severity == DeltaDriverSeverity.Critical && _gate < DeltaGateLevel.G4)
_gate = DeltaGateLevel.G4;
else if (driver.Severity == DeltaDriverSeverity.High && _gate < DeltaGateLevel.G3)
_gate = DeltaGateLevel.G3;
return this;
}
public DeltaVerdictBuilder AddWarningDriver(DeltaDriver driver)
{
_warningDrivers.Add(driver);
if (_status == DeltaVerdictStatus.Pass)
_status = DeltaVerdictStatus.Warn;
// Escalate gate for medium severity warnings
if (driver.Severity >= DeltaDriverSeverity.Medium && _gate < DeltaGateLevel.G2)
_gate = DeltaGateLevel.G2;
return this;
}
public DeltaVerdictBuilder AddException(string exceptionId)
{
_exceptions.Add(exceptionId);
return this;
}
public DeltaVerdictBuilder AddRecommendation(string recommendation)
{
_recommendations.Add(recommendation);
return this;
}
public DeltaVerdictBuilder WithExplanation(string explanation)
{
_explanation = explanation;
return this;
}
public DeltaVerdict Build(string deltaId)
{
// If all blocking drivers are excepted, change to PassWithExceptions
if (_status == DeltaVerdictStatus.Fail &&
_blockingDrivers.Count > 0 &&
_exceptions.Count >= _blockingDrivers.Count)
{
_status = DeltaVerdictStatus.PassWithExceptions;
}
var blockingDrivers = _blockingDrivers.ToList();
var warningDrivers = _warningDrivers.ToList();
var appliedExceptions = _exceptions.ToList();
// Compute content-addressed VerdictId from inputs
var verdictId = _idGenerator.ComputeVerdictId(
deltaId,
blockingDrivers,
warningDrivers,
appliedExceptions,
_gate);
return new DeltaVerdict
{
VerdictId = verdictId,
DeltaId = deltaId,
EvaluatedAt = DateTimeOffset.UtcNow,
Status = _status,
RecommendedGate = _gate,
RiskPoints = _riskPoints,
BlockingDrivers = blockingDrivers,
WarningDrivers = warningDrivers,
AppliedExceptions = appliedExceptions,
Explanation = _explanation ?? GenerateExplanation(),
Recommendations = _recommendations.ToList()
};
}
private string GenerateExplanation()
{
return _status switch
{
DeltaVerdictStatus.Pass => "No blocking changes detected",
DeltaVerdictStatus.Warn => $"{_warningDrivers.Count} warning(s) detected",
DeltaVerdictStatus.Fail => $"{_blockingDrivers.Count} blocking issue(s) detected",
DeltaVerdictStatus.PassWithExceptions => $"Blocked by {_blockingDrivers.Count} issue(s), covered by exceptions",
_ => "Unknown status"
};
}
}