/** * 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 }; } }