using Microsoft.Extensions.Logging;
using StellaOps.Policy.Determinization.Models;
namespace StellaOps.Policy.Engine.Gates.Determinization;
///
/// Builds signal snapshots for determinization evaluation by querying signal repositories.
///
public sealed class SignalSnapshotBuilder : ISignalSnapshotBuilder
{
private readonly ISignalRepository _signalRepository;
private readonly TimeProvider _timeProvider;
private readonly ILogger _logger;
public SignalSnapshotBuilder(
ISignalRepository signalRepository,
TimeProvider timeProvider,
ILogger logger)
{
_signalRepository = signalRepository;
_timeProvider = timeProvider;
_logger = logger;
}
public async Task BuildAsync(
string cveId,
string componentPurl,
CancellationToken ct = default)
{
ArgumentException.ThrowIfNullOrWhiteSpace(cveId);
ArgumentException.ThrowIfNullOrWhiteSpace(componentPurl);
_logger.LogDebug(
"Building signal snapshot for CVE {CveId} on {Purl}",
cveId,
componentPurl);
var snapshotAt = _timeProvider.GetUtcNow();
var subjectKey = BuildSubjectKey(cveId, componentPurl);
// Query all signals in parallel
var signalsTask = _signalRepository.GetSignalsAsync(subjectKey, ct);
var signals = await signalsTask;
// Build snapshot from retrieved signals
var snapshot = SignalSnapshot.Empty(cveId, componentPurl, snapshotAt);
foreach (var signal in signals)
{
snapshot = ApplySignal(snapshot, signal);
}
_logger.LogDebug(
"Built signal snapshot for CVE {CveId} on {Purl}: {SignalCount} signals present",
cveId,
componentPurl,
signals.Count);
return snapshot;
}
private static string BuildSubjectKey(string cveId, string componentPurl)
=> $"{cveId}::{componentPurl}";
private SignalSnapshot ApplySignal(SignalSnapshot snapshot, Signal signal)
{
// This is a placeholder implementation
// In a real implementation, this would map Signal objects to SignalState instances
// based on signal type and update the appropriate field in the snapshot
return snapshot;
}
}
///
/// Repository for retrieving signals.
///
public interface ISignalRepository
{
///
/// Get all signals for the given subject key.
///
Task> GetSignalsAsync(string subjectKey, CancellationToken ct = default);
}
///
/// Represents a signal retrieved from storage.
///
public sealed record Signal
{
public required string Type { get; init; }
public required string SubjectKey { get; init; }
public required DateTimeOffset ObservedAt { get; init; }
public required object? Evidence { get; init; }
}