/**
* 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;
///
/// Deterministic resolver that guarantees reproducible results.
///
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";
}
///
public ResolutionResult Run(EvidenceGraph graph)
=> Run(graph, DateTimeOffset.UtcNow);
///
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();
var verdictList = new List();
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();
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
};
}
///
/// Gathers all inbound edges for a node (edges where Dst == nodeId).
///
private static IReadOnlyList GatherInboundEvidence(EvidenceGraph graph, NodeId nodeId)
{
return graph.Edges
.Where(e => e.Dst == nodeId)
.OrderBy(e => e.Id) // Deterministic ordering
.ToList();
}
///
/// Pure evaluation function - no IO allowed.
///
private Verdict EvaluatePure(
Node node,
IReadOnlyList inboundEdges,
Policy policy,
IReadOnlyDictionary predecessorVerdicts,
int traversalIndex)
{
return _evaluator.Evaluate(node, inboundEdges, policy, predecessorVerdicts) with
{
TraversalIndex = traversalIndex
};
}
}