154 lines
4.9 KiB
C#
154 lines
4.9 KiB
C#
/**
|
|
* DeterministicResolver - Core Implementation
|
|
* Sprint: SPRINT_9100_0001_0001 (Core Resolver Package)
|
|
* Tasks: RESOLVER-9100-010, RESOLVER-9100-011, RESOLVER-9100-012, RESOLVER-9100-013, RESOLVER-9100-014
|
|
*
|
|
* Main resolver implementation providing:
|
|
* - Deterministic graph canonicalization
|
|
* - Ordered traversal
|
|
* - Per-node evaluation
|
|
* - Digest computation
|
|
*/
|
|
|
|
using System.Collections.Immutable;
|
|
|
|
namespace StellaOps.Resolver;
|
|
|
|
/// <summary>
|
|
/// Deterministic resolver that guarantees reproducible results.
|
|
/// </summary>
|
|
public sealed class DeterministicResolver : IDeterministicResolver
|
|
{
|
|
private readonly Policy _policy;
|
|
private readonly IGraphOrderer _orderer;
|
|
private readonly ITrustLatticeEvaluator _evaluator;
|
|
private readonly IFinalDigestComputer _digestComputer;
|
|
private readonly IGraphValidator _validator;
|
|
private readonly string _version;
|
|
|
|
public DeterministicResolver(
|
|
Policy policy,
|
|
IGraphOrderer orderer,
|
|
ITrustLatticeEvaluator evaluator,
|
|
IFinalDigestComputer? digestComputer = null,
|
|
IGraphValidator? validator = null,
|
|
string? version = null)
|
|
{
|
|
ArgumentNullException.ThrowIfNull(policy);
|
|
ArgumentNullException.ThrowIfNull(orderer);
|
|
ArgumentNullException.ThrowIfNull(evaluator);
|
|
|
|
_policy = policy;
|
|
_orderer = orderer;
|
|
_evaluator = evaluator;
|
|
_digestComputer = digestComputer ?? new Sha256FinalDigestComputer();
|
|
_validator = validator ?? new DefaultGraphValidator();
|
|
_version = version ?? "1.0.0";
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public ResolutionResult Run(EvidenceGraph graph)
|
|
=> Run(graph, DateTimeOffset.UtcNow);
|
|
|
|
/// <inheritdoc/>
|
|
public ResolutionResult Run(EvidenceGraph graph, DateTimeOffset resolvedAt)
|
|
{
|
|
ArgumentNullException.ThrowIfNull(graph);
|
|
|
|
// Phase 1: Validate graph
|
|
var validationResult = _validator.Validate(graph);
|
|
if (!validationResult.IsValid)
|
|
{
|
|
throw new InvalidGraphException(validationResult);
|
|
}
|
|
|
|
// Phase 2: Compute traversal order
|
|
var traversalOrder = _orderer.OrderNodes(graph);
|
|
|
|
// Phase 3: Evaluate each node in order
|
|
var verdicts = new Dictionary<NodeId, Verdict>();
|
|
var verdictList = new List<Verdict>();
|
|
|
|
for (var i = 0; i < traversalOrder.Count; i++)
|
|
{
|
|
var nodeId = traversalOrder[i];
|
|
var node = graph.GetNode(nodeId);
|
|
|
|
if (node is null)
|
|
{
|
|
// Node referenced but not in graph - this should be caught by validation
|
|
continue;
|
|
}
|
|
|
|
// Gather inbound evidence (edges where Dst == nodeId)
|
|
var inboundEdges = GatherInboundEvidence(graph, nodeId);
|
|
|
|
// Build predecessor verdicts dictionary
|
|
var predecessorVerdicts = new Dictionary<NodeId, Verdict>();
|
|
foreach (var edge in inboundEdges)
|
|
{
|
|
if (verdicts.TryGetValue(edge.Src, out var srcVerdict))
|
|
{
|
|
predecessorVerdicts[edge.Src] = srcVerdict;
|
|
}
|
|
}
|
|
|
|
// Evaluate pure (no IO)
|
|
var verdict = EvaluatePure(node, inboundEdges, _policy, predecessorVerdicts, i);
|
|
|
|
verdicts[nodeId] = verdict;
|
|
verdictList.Add(verdict);
|
|
}
|
|
|
|
// Phase 4: Compute final digest
|
|
var verdictEntries = verdictList
|
|
.Select(v => new VerdictDigestEntry(v.Node.Value, v.VerdictDigest))
|
|
.ToImmutableArray();
|
|
|
|
var digestInput = new DigestInput(
|
|
graph.GraphDigest,
|
|
_policy.Digest,
|
|
verdictEntries);
|
|
|
|
var finalDigest = _digestComputer.Compute(digestInput);
|
|
|
|
return new ResolutionResult
|
|
{
|
|
TraversalSequence = traversalOrder.ToImmutableArray(),
|
|
Verdicts = verdictList.ToImmutableArray(),
|
|
GraphDigest = graph.GraphDigest,
|
|
PolicyDigest = _policy.Digest,
|
|
FinalDigest = finalDigest,
|
|
ResolvedAt = resolvedAt,
|
|
ResolverVersion = _version
|
|
};
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gathers all inbound edges for a node (edges where Dst == nodeId).
|
|
/// </summary>
|
|
private static IReadOnlyList<Edge> GatherInboundEvidence(EvidenceGraph graph, NodeId nodeId)
|
|
{
|
|
return graph.Edges
|
|
.Where(e => e.Dst == nodeId)
|
|
.OrderBy(e => e.Id) // Deterministic ordering
|
|
.ToList();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Pure evaluation function - no IO allowed.
|
|
/// </summary>
|
|
private Verdict EvaluatePure(
|
|
Node node,
|
|
IReadOnlyList<Edge> inboundEdges,
|
|
Policy policy,
|
|
IReadOnlyDictionary<NodeId, Verdict> predecessorVerdicts,
|
|
int traversalIndex)
|
|
{
|
|
return _evaluator.Evaluate(node, inboundEdges, policy, predecessorVerdicts) with
|
|
{
|
|
TraversalIndex = traversalIndex
|
|
};
|
|
}
|
|
}
|