using System.Collections.Immutable; using System.Diagnostics; namespace StellaOps.Policy.Engine.DeterminismGuard; /// /// Service that enforces determinism constraints during policy evaluation. /// Combines static analysis and runtime monitoring. /// public sealed class DeterminismGuardService { private readonly ProhibitedPatternAnalyzer _analyzer; private readonly DeterminismGuardOptions _options; private readonly RuntimeDeterminismMonitor _runtimeMonitor; public DeterminismGuardService(DeterminismGuardOptions? options = null) { _options = options ?? DeterminismGuardOptions.Default; _analyzer = new ProhibitedPatternAnalyzer(); _runtimeMonitor = new RuntimeDeterminismMonitor(_options); } /// /// Analyzes source code for determinism violations. /// public DeterminismAnalysisResult AnalyzeSource(string sourceCode, string? fileName = null) { return _analyzer.AnalyzeSource(sourceCode, fileName, _options); } /// /// Creates a guarded execution scope for policy evaluation. /// public EvaluationScope CreateScope(string scopeId, DateTimeOffset evaluationTimestamp) { return new EvaluationScope(scopeId, evaluationTimestamp, _options, _runtimeMonitor); } /// /// Validates that a policy evaluation context is deterministic. /// public DeterminismAnalysisResult ValidateContext(TContext context, string contextName) { var stopwatch = Stopwatch.StartNew(); var violations = new List(); // Check for null if (context is null) { violations.Add(new DeterminismViolation { Category = DeterminismViolationCategory.Other, ViolationType = "NullContext", Message = $"Evaluation context '{contextName}' is null", Severity = DeterminismViolationSeverity.Error, Remediation = "Provide a valid evaluation context" }); } stopwatch.Stop(); var countBySeverity = violations .GroupBy(v => v.Severity) .ToImmutableDictionary(g => g.Key, g => g.Count()); var hasBlockingViolation = violations.Any(v => v.Severity >= _options.FailOnSeverity); var passed = !_options.EnforcementEnabled || !hasBlockingViolation; return new DeterminismAnalysisResult { Passed = passed, Violations = violations.ToImmutableArray(), CountBySeverity = countBySeverity, AnalysisDurationMs = stopwatch.ElapsedMilliseconds, EnforcementEnabled = _options.EnforcementEnabled }; } /// /// Gets a determinism-safe time provider that only returns injected timestamps. /// public DeterministicTimeProvider GetTimeProvider(DateTimeOffset fixedTimestamp) { return new DeterministicTimeProvider(fixedTimestamp); } } /// /// A guarded scope for policy evaluation that tracks determinism violations. /// public sealed class EvaluationScope : IDisposable { private readonly string _scopeId; private readonly DateTimeOffset _evaluationTimestamp; private readonly DeterminismGuardOptions _options; private readonly RuntimeDeterminismMonitor _monitor; private readonly Stopwatch _stopwatch; private readonly List _violations; private bool _disposed; internal EvaluationScope( string scopeId, DateTimeOffset evaluationTimestamp, DeterminismGuardOptions options, RuntimeDeterminismMonitor monitor) { _scopeId = scopeId ?? throw new ArgumentNullException(nameof(scopeId)); _evaluationTimestamp = evaluationTimestamp; _options = options; _monitor = monitor; _stopwatch = Stopwatch.StartNew(); _violations = new List(); if (_options.EnableRuntimeMonitoring) { _monitor.EnterScope(scopeId); } } /// /// Scope identifier for tracing. /// public string ScopeId => _scopeId; /// /// The fixed evaluation timestamp for this scope. /// public DateTimeOffset EvaluationTimestamp => _evaluationTimestamp; /// /// Reports a runtime violation detected during evaluation. /// public void ReportViolation(DeterminismViolation violation) { ArgumentNullException.ThrowIfNull(violation); lock (_violations) { _violations.Add(violation); } if (_options.EnforcementEnabled && violation.Severity >= _options.FailOnSeverity) { throw new DeterminismViolationException(violation); } } /// /// Gets the current timestamp (always returns the fixed evaluation timestamp). /// public DateTimeOffset GetTimestamp() => _evaluationTimestamp; /// /// Gets all violations recorded in this scope. /// public IReadOnlyList GetViolations() { lock (_violations) { return _violations.ToList(); } } /// /// Completes the scope and returns analysis results. /// public DeterminismAnalysisResult Complete() { _stopwatch.Stop(); IReadOnlyList allViolations; lock (_violations) { allViolations = _violations.ToList(); } var countBySeverity = allViolations .GroupBy(v => v.Severity) .ToImmutableDictionary(g => g.Key, g => g.Count()); var hasBlockingViolation = allViolations.Any(v => v.Severity >= _options.FailOnSeverity); var passed = !_options.EnforcementEnabled || !hasBlockingViolation; return new DeterminismAnalysisResult { Passed = passed, Violations = allViolations.ToImmutableArray(), CountBySeverity = countBySeverity, AnalysisDurationMs = _stopwatch.ElapsedMilliseconds, EnforcementEnabled = _options.EnforcementEnabled }; } public void Dispose() { if (_disposed) { return; } _disposed = true; if (_options.EnableRuntimeMonitoring) { _monitor.ExitScope(_scopeId); } } } /// /// Exception thrown when a determinism violation is detected with enforcement enabled. /// public sealed class DeterminismViolationException : Exception { public DeterminismViolationException(DeterminismViolation violation) : base($"Determinism violation: {violation.Message}") { Violation = violation; } public DeterminismViolation Violation { get; } } /// /// Time provider that always returns a fixed timestamp. /// public sealed class DeterministicTimeProvider : TimeProvider { private readonly DateTimeOffset _fixedTimestamp; public DeterministicTimeProvider(DateTimeOffset fixedTimestamp) { _fixedTimestamp = fixedTimestamp; } public override DateTimeOffset GetUtcNow() => _fixedTimestamp; public override TimeZoneInfo LocalTimeZone => TimeZoneInfo.Utc; } /// /// Runtime monitor for detecting non-deterministic operations. /// internal sealed class RuntimeDeterminismMonitor { private readonly DeterminismGuardOptions _options; private readonly HashSet _activeScopes = new(StringComparer.Ordinal); private readonly object _lock = new(); public RuntimeDeterminismMonitor(DeterminismGuardOptions options) { _options = options; } public void EnterScope(string scopeId) { lock (_lock) { _activeScopes.Add(scopeId); } } public void ExitScope(string scopeId) { lock (_lock) { _activeScopes.Remove(scopeId); } } public bool IsInScope => _activeScopes.Count > 0; /// /// Checks if we're in a guarded scope and should intercept operations. /// public bool ShouldIntercept() { return _options.EnableRuntimeMonitoring && IsInScope; } } /// /// Extension methods for integrating determinism guard with evaluation. /// public static class DeterminismGuardExtensions { /// /// Executes an evaluation function within a determinism-guarded scope. /// public static TResult ExecuteGuarded( this DeterminismGuardService guard, string scopeId, DateTimeOffset evaluationTimestamp, Func evaluation) { ArgumentNullException.ThrowIfNull(guard); ArgumentNullException.ThrowIfNull(evaluation); using var scope = guard.CreateScope(scopeId, evaluationTimestamp); try { return evaluation(scope); } finally { var result = scope.Complete(); if (!result.Passed) { // Log violations even if not throwing foreach (var violation in result.Violations) { // In production, this would log to structured logging System.Diagnostics.Debug.WriteLine( $"[DeterminismGuard] {violation.Severity}: {violation.Message}"); } } } } /// /// Executes an async evaluation function within a determinism-guarded scope. /// public static async Task ExecuteGuardedAsync( this DeterminismGuardService guard, string scopeId, DateTimeOffset evaluationTimestamp, Func> evaluation) { ArgumentNullException.ThrowIfNull(guard); ArgumentNullException.ThrowIfNull(evaluation); using var scope = guard.CreateScope(scopeId, evaluationTimestamp); try { return await evaluation(scope).ConfigureAwait(false); } finally { var result = scope.Complete(); if (!result.Passed) { foreach (var violation in result.Violations) { System.Diagnostics.Debug.WriteLine( $"[DeterminismGuard] {violation.Severity}: {violation.Message}"); } } } } }