Files
git.stella-ops.org/src/__Libraries/StellaOps.ElkSharp/ElkEdgeRouterIterative.StrategyRepair.Evaluate.Helpers.cs
master 8a28e25d05 Decompose EvaluateStrategy (644->480 lines) and close sprint 006 TASK-002/003/004
Extract BuildMaxRetryState, DetectStrategyStagnation, and DecideStrategyAttemptOutcome
into ElkEdgeRouterIterative.StrategyRepair.Evaluate.Helpers.cs (174 lines).

Sprint 006 status: TASK-002 DONE (hybrid parity coverage), TASK-003 DONE (file
decomposition), TASK-004 DONE (docs). TASK-001 remains DOING (conflict-zone scheduling).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-01 14:24:16 +03:00

175 lines
6.9 KiB
C#

using System.Diagnostics;
namespace StellaOps.ElkSharp;
internal static partial class ElkEdgeRouterIterative
{
/// <summary>
/// Creates a worst-case retry state used as the initial "best" baseline
/// in the strategy evaluation loop.
/// </summary>
private static RoutingRetryState BuildMaxRetryState()
{
return new RoutingRetryState(
RemainingShortHighways: int.MaxValue,
RepeatCollectorCorridorViolations: int.MaxValue,
RepeatCollectorNodeClearanceViolations: int.MaxValue,
TargetApproachJoinViolations: int.MaxValue,
TargetApproachBacktrackingViolations: int.MaxValue,
ExcessiveDetourViolations: int.MaxValue,
SharedLaneViolations: int.MaxValue,
BoundarySlotViolations: int.MaxValue,
BelowGraphViolations: int.MaxValue,
UnderNodeViolations: int.MaxValue,
LongDiagonalViolations: int.MaxValue,
ProximityViolations: int.MaxValue,
EntryAngleViolations: int.MaxValue,
GatewaySourceExitViolations: int.MaxValue,
LabelProximityViolations: int.MaxValue,
EdgeCrossings: int.MaxValue);
}
/// <summary>
/// Checks stagnation conditions: repeated repair focus, repeated plateau
/// fingerprints, and blocking cycle detection. Returns the stagnation
/// outcome reason or null if the attempt should continue.
/// </summary>
private static string? DetectStrategyStagnation(
RepairPlan? repairPlan,
RoutingRetryState retryState,
RoutingRetryState bestAttemptRetryState,
int attempt,
ref string? lastRepairFocusFingerprint,
ref int repeatedRepairFocusCount,
ref string? lastPlateauFingerprint,
ref int repeatedPlateauFingerprintCount,
List<string> recentBlockingCycleFingerprints)
{
var repairFocusFingerprint = BuildRepairFocusFingerprint(repairPlan);
if (!string.IsNullOrEmpty(repairFocusFingerprint))
{
if (string.Equals(repairFocusFingerprint, lastRepairFocusFingerprint, StringComparison.Ordinal))
{
repeatedRepairFocusCount++;
}
else
{
lastRepairFocusFingerprint = repairFocusFingerprint;
repeatedRepairFocusCount = 0;
}
if (repeatedRepairFocusCount >= 1 && attempt >= 3)
{
return $"stalled-same-focus({DescribeRetryState(retryState)})@attempt{attempt + 1}";
}
}
else
{
lastRepairFocusFingerprint = null;
repeatedRepairFocusCount = 0;
}
var plateauFingerprint = BuildPlateauFingerprint(retryState, repairPlan);
if (string.Equals(plateauFingerprint, lastPlateauFingerprint, StringComparison.Ordinal))
{
repeatedPlateauFingerprintCount++;
}
else
{
lastPlateauFingerprint = plateauFingerprint;
repeatedPlateauFingerprintCount = 0;
}
if (repeatedPlateauFingerprintCount >= 2 && attempt >= 3)
{
return $"stalled-repeat({DescribeRetryState(retryState)})@attempt{attempt + 1}";
}
var blockingCycleFingerprint = BuildBlockingCycleFingerprint(retryState, repairPlan);
if (ShouldStopForBlockingCycle(
recentBlockingCycleFingerprints,
blockingCycleFingerprint,
retryState,
bestAttemptRetryState,
attempt))
{
return $"stalled-cycle({DescribeRetryState(retryState)})@attempt{attempt + 1}";
}
AppendRecentFingerprint(recentBlockingCycleFingerprints, blockingCycleFingerprint, 4);
return null;
}
/// <summary>
/// Determines the retry/accept decision for a strategy attempt based on
/// violation categories. Returns the attempt outcome string and whether
/// the loop should continue, break, or accept.
/// </summary>
private static (string Outcome, bool ShouldContinue, bool IsValid) DecideStrategyAttemptOutcome(
EdgeRoutingScore score,
RoutingRetryState retryState,
int maxAllowedNodeCrossings,
int attempt,
int maxAttempts,
StrategyWorkItem workItem,
RoutingStrategy strategy)
{
if (score.NodeCrossings > maxAllowedNodeCrossings)
{
strategy.AdaptForViolations(score, attempt, retryState);
return ($"hard-violation(nc={score.NodeCrossings}>{maxAllowedNodeCrossings})@attempt{attempt + 1}", true, false);
}
if (retryState.RemainingShortHighways > 0
|| retryState.RepeatCollectorCorridorViolations > 0
|| retryState.RepeatCollectorNodeClearanceViolations > 0
|| retryState.TargetApproachJoinViolations > 0
|| retryState.TargetApproachBacktrackingViolations > 0
|| retryState.SharedLaneViolations > 0
|| retryState.BelowGraphViolations > 0
|| retryState.UnderNodeViolations > 0
|| retryState.LongDiagonalViolations > 0
|| retryState.EntryAngleViolations > 0
|| retryState.GatewaySourceExitViolations > 0)
{
if (ShouldRetryForPrimaryViolations(retryState, attempt, maxAttempts))
{
strategy.AdaptForViolations(score, attempt, retryState);
return ($"retry({DescribeRetryState(retryState)})@attempt{attempt + 1}", true, false);
}
return ($"invalid({DescribeRetryState(retryState)})@attempt{attempt + 1}", false, false);
}
if (retryState.RequiresLengthRetry)
{
if (ShouldRetryForPrimaryViolations(retryState, attempt, maxAttempts))
{
strategy.AdaptForViolations(score, attempt, retryState);
return ($"retry({DescribeRetryState(retryState)})@attempt{attempt + 1}", true, false);
}
return ($"invalid({DescribeRetryState(retryState)})@attempt{attempt + 1}", false, false);
}
if (retryState.RequiresQualityRetry
&& ShouldRetryForPrimaryViolations(retryState, attempt, maxAttempts))
{
strategy.AdaptForViolations(score, attempt, retryState);
return ($"retry({DescribeRetryState(retryState)})@attempt{attempt + 1}", true, false);
}
if (ShouldRetryForEdgeCrossings(retryState, attempt, maxAttempts))
{
strategy.AdaptForViolations(score, attempt, retryState);
return ($"retry(edge-crossings={retryState.EdgeCrossings})@attempt{attempt + 1}", true, false);
}
var residualSoftViolations = retryState.RequiresQualityRetry || retryState.EdgeCrossings > 0;
var validOutcome = residualSoftViolations
? $"valid-soft({DescribeRetryState(retryState)})@attempt{attempt + 1}"
: $"valid@attempt{attempt + 1}";
return (validOutcome, false, true);
}
}