diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistCoachDashboardWidget.cpp b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistCoachDashboardWidget.cpp index f8132a3..c3b4586 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistCoachDashboardWidget.cpp +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistCoachDashboardWidget.cpp @@ -4964,6 +4964,41 @@ FHyperTwistCoachDashboardQueueRecoveryWeighting UHyperTwistCoachDashboardWidget: HyperTwistCoachDashboardWidgetInternal::DeriveMethodDrillLaunchRecoveryBias( HandoffHistoryBias ); + Weighting.MethodDrillUpstreamHistoryCount = + CachedCoachPanelState.MethodDrillRecoveryHistoryCount; + Weighting.MethodDrillUpstreamStrongCount = + CachedCoachPanelState.StrongMethodDrillRecoveryHistoryCount; + Weighting.MethodDrillUpstreamWeakCount = + CachedCoachPanelState.WeakMethodDrillRecoveryHistoryCount; + Weighting.MethodDrillUpstreamAverageOutcomeScore = + CachedCoachPanelState.MethodDrillRecoveryAverageOutcomeScore; + const bool bStrongMethodDrillRecoveryMemory = + Weighting.MethodDrillUpstreamHistoryCount >= 2 + && Weighting.MethodDrillUpstreamStrongCount >= Weighting.MethodDrillUpstreamWeakCount + && Weighting.MethodDrillUpstreamAverageOutcomeScore >= 55.0f; + const bool bWeakMethodDrillRecoveryMemory = + Weighting.MethodDrillUpstreamHistoryCount >= 2 + && Weighting.MethodDrillUpstreamWeakCount > Weighting.MethodDrillUpstreamStrongCount + && Weighting.MethodDrillUpstreamAverageOutcomeScore <= 45.0f; + if (bStrongMethodDrillRecoveryMemory) + { + Weighting.MethodDrillLaunchBiasCount += FMath::Clamp( + Weighting.MethodDrillUpstreamStrongCount - Weighting.MethodDrillUpstreamWeakCount + + (Weighting.MethodDrillUpstreamAverageOutcomeScore >= 75.0f ? 1 : 0), + 1, + 3 + ); + Weighting.bHasMethodDrillMemorySignal = true; + } + else if (bWeakMethodDrillRecoveryMemory) + { + Weighting.MethodDrillPromoteBiasCount += FMath::Clamp( + Weighting.MethodDrillUpstreamWeakCount - Weighting.MethodDrillUpstreamStrongCount, + 1, + 3 + ); + Weighting.bHasMethodDrillMemorySignal = true; + } if (CachedCoachPanelState.PreferredGuidanceSourceLabel == TEXT("coach-memory-method-drill-follow-up-strong")) { @@ -4978,7 +5013,8 @@ FHyperTwistCoachDashboardQueueRecoveryWeighting UHyperTwistCoachDashboardWidget: Weighting.bHasMethodDrillSignal = Weighting.MethodDrillLaunchBiasCount > 0 || Weighting.MethodDrillPromoteBiasCount > 0 - || Weighting.bHasMethodDrillOutcomeSignal; + || Weighting.bHasMethodDrillOutcomeSignal + || Weighting.bHasMethodDrillMemorySignal; const bool bHasDirectOutcomeSignal = Weighting.PromoteCount > 0 || Weighting.LaunchCount > 0; Weighting.bHasSignal = bHasDirectOutcomeSignal || Weighting.bHasMethodDrillSignal; @@ -6270,6 +6306,17 @@ void UHyperTwistCoachDashboardWidget::UpdateDashboardPresentation() bool bHandoffRecommendationUsedDeferredOverflowHistory = false; bool bHandoffRecommendationUsedDeferredFirstSuppressionSignal = false; bool bHandoffRecommendationUsedMethodDrillWeighting = false; + bool bHandoffRecommendationUsedMethodDrillRecoveryMemory = false; + const bool bStrongMethodDrillRecoveryMemory = + CachedCoachPanelState.MethodDrillRecoveryHistoryCount >= 2 + && CachedCoachPanelState.StrongMethodDrillRecoveryHistoryCount + >= CachedCoachPanelState.WeakMethodDrillRecoveryHistoryCount + && CachedCoachPanelState.MethodDrillRecoveryAverageOutcomeScore >= 55.0f; + const bool bWeakMethodDrillRecoveryMemory = + CachedCoachPanelState.MethodDrillRecoveryHistoryCount >= 2 + && CachedCoachPanelState.WeakMethodDrillRecoveryHistoryCount + > CachedCoachPanelState.StrongMethodDrillRecoveryHistoryCount + && CachedCoachPanelState.MethodDrillRecoveryAverageOutcomeScore <= 45.0f; if (!bCanUseCoachHandoff) { CoachHandoffRecommendationStatusLine = @@ -6358,6 +6405,42 @@ void UHyperTwistCoachDashboardWidget::UpdateDashboardPresentation() bHandoffRecommendationUsedMethodDrillWeighting = true; } } + else if (bStrongMethodDrillRecoveryMemory + && (bFollowUpReadyForHandoff + || (bQueueReadyForHandoff && bQueueLooksFollowUpOrRecovery))) + { + if (HandoffHistoryBias.PreferredKind + == HyperTwistCoachDashboardWidgetInternal::EHandoffRecommendationKind::Queue + && bQueueReadyForHandoff + && bQueueLooksFollowUpOrRecovery) + { + HandoffRecommendationKind = + HyperTwistCoachDashboardWidgetInternal::EHandoffRecommendationKind::Queue; + bHandoffRecommendationUsedHistory = true; + bHandoffRecommendationUsedMethodDrillRecoveryMemory = true; + } + else if (HandoffHistoryBias.PreferredKind + == HyperTwistCoachDashboardWidgetInternal::EHandoffRecommendationKind::FollowUp + && bFollowUpReadyForHandoff) + { + HandoffRecommendationKind = + HyperTwistCoachDashboardWidgetInternal::EHandoffRecommendationKind::FollowUp; + bHandoffRecommendationUsedHistory = true; + bHandoffRecommendationUsedMethodDrillRecoveryMemory = true; + } + else if (bQueueReadyForHandoff && bQueueLooksFollowUpOrRecovery) + { + HandoffRecommendationKind = + HyperTwistCoachDashboardWidgetInternal::EHandoffRecommendationKind::Queue; + bHandoffRecommendationUsedMethodDrillRecoveryMemory = true; + } + else if (bFollowUpReadyForHandoff) + { + HandoffRecommendationKind = + HyperTwistCoachDashboardWidgetInternal::EHandoffRecommendationKind::FollowUp; + bHandoffRecommendationUsedMethodDrillRecoveryMemory = true; + } + } else if (MethodDrillFollowUpWeighting.IsStructurallyValid() && MethodDrillFollowUpWeighting.bPreferCoachHandoff && (bRecommendedReadyForHandoff @@ -6405,6 +6488,42 @@ void UHyperTwistCoachDashboardWidget::UpdateDashboardPresentation() bHandoffRecommendationUsedMethodDrillWeighting = true; } } + else if (bWeakMethodDrillRecoveryMemory + && (bRecommendedReadyForHandoff + || (bQueueReadyForHandoff && bQueueLooksRecommendedOrPrimary))) + { + if (HandoffHistoryBias.PreferredKind + == HyperTwistCoachDashboardWidgetInternal::EHandoffRecommendationKind::Recommended + && bRecommendedReadyForHandoff) + { + HandoffRecommendationKind = + HyperTwistCoachDashboardWidgetInternal::EHandoffRecommendationKind::Recommended; + bHandoffRecommendationUsedHistory = true; + bHandoffRecommendationUsedMethodDrillRecoveryMemory = true; + } + else if (HandoffHistoryBias.PreferredKind + == HyperTwistCoachDashboardWidgetInternal::EHandoffRecommendationKind::Queue + && bQueueReadyForHandoff + && bQueueLooksRecommendedOrPrimary) + { + HandoffRecommendationKind = + HyperTwistCoachDashboardWidgetInternal::EHandoffRecommendationKind::Queue; + bHandoffRecommendationUsedHistory = true; + bHandoffRecommendationUsedMethodDrillRecoveryMemory = true; + } + else if (bRecommendedReadyForHandoff) + { + HandoffRecommendationKind = + HyperTwistCoachDashboardWidgetInternal::EHandoffRecommendationKind::Recommended; + bHandoffRecommendationUsedMethodDrillRecoveryMemory = true; + } + else if (bQueueReadyForHandoff && bQueueLooksRecommendedOrPrimary) + { + HandoffRecommendationKind = + HyperTwistCoachDashboardWidgetInternal::EHandoffRecommendationKind::Queue; + bHandoffRecommendationUsedMethodDrillRecoveryMemory = true; + } + } else if (HandoffClosureBias.bHasSignal && HandoffClosureBias.ContinuationBias >= HandoffClosureBias.SuppressionBias + 2) { @@ -6594,10 +6713,11 @@ void UHyperTwistCoachDashboardWidget::UpdateDashboardPresentation() bDisplayedCoachHandoffRecommendationUsedDeferredFirstSuppressionSignal = bHandoffRecommendationUsedDeferredFirstSuppressionSignal; bDisplayedCoachHandoffRecommendationUsedMethodDrillWeighting = - bHandoffRecommendationUsedMethodDrillWeighting; + bHandoffRecommendationUsedMethodDrillWeighting + || bHandoffRecommendationUsedMethodDrillRecoveryMemory; CoachHandoffRecommendationDetailLine = FString::Printf( - TEXT("Closure continuation bias %d | Closure suppression bias %d | Queue ready: %s | Follow-up ready: %s | Recommended ready: %s | Preferred lane: %s | Memory source: %s%s%s%s%s%s | Launch bias: %s from %s | broadened/tightened history %d/%d | deferred tightened launches %d | deferred launch cases %d%s | Handoff history preferred: %s | accepted q/f/r %d/%d/%d | deferred-overflow accepted q/f/r %d/%d/%d | deferred-first recommendation q/f/r %d/%d/%d | recommended q/f/r %d/%d/%d"), + TEXT("Closure continuation bias %d | Closure suppression bias %d | Queue ready: %s | Follow-up ready: %s | Recommended ready: %s | Preferred lane: %s | Memory source: %s%s%s%s%s%s%s | Launch bias: %s from %s | broadened/tightened history %d/%d | deferred tightened launches %d | deferred launch cases %d%s | Handoff history preferred: %s | accepted q/f/r %d/%d/%d | deferred-overflow accepted q/f/r %d/%d/%d | deferred-first recommendation q/f/r %d/%d/%d | recommended q/f/r %d/%d/%d"), HandoffClosureBias.ContinuationBias, HandoffClosureBias.SuppressionBias, bQueueReadyForHandoff ? TEXT("yes") : TEXT("no"), @@ -6620,6 +6740,11 @@ void UHyperTwistCoachDashboardWidget::UpdateDashboardPresentation() ? TEXT(" | Retained drill-follow-up history directly tipped this handoff toward narrow continuation.") : TEXT(" | Retained drill-follow-up history directly tipped this handoff back toward broader coach guidance.")) : TEXT(""), + bHandoffRecommendationUsedMethodDrillRecoveryMemory + ? (bStrongMethodDrillRecoveryMemory + ? TEXT(" | Upstream strong drill-driven recovery memory directly tipped this handoff toward narrow continuation.") + : TEXT(" | Upstream weak drill-driven recovery memory directly tipped this handoff back toward broader coach guidance.")) + : TEXT(""), bHandoffRecommendationUsedDeferredFirstSuppressionSignal ? (bDeferredFirstSuppressionSignalPromoted ? TEXT(" | Deferred-first queue suppression is promoted and keeping narrowed follow-up continuation ahead.") @@ -6652,6 +6777,13 @@ void UHyperTwistCoachDashboardWidget::UpdateDashboardPresentation() HandoffHistoryBias.FollowUpRecommendationCount, HandoffHistoryBias.RecommendedRecommendationCount ); + CoachHandoffRecommendationDetailLine += FString::Printf( + TEXT(" | upstream drill recovery strong/weak/history %d/%d/%d avg %.1f"), + CachedCoachPanelState.StrongMethodDrillRecoveryHistoryCount, + CachedCoachPanelState.WeakMethodDrillRecoveryHistoryCount, + CachedCoachPanelState.MethodDrillRecoveryHistoryCount, + CachedCoachPanelState.MethodDrillRecoveryAverageOutcomeScore + ); if (bCanLaunchMethodDrillFollowUp && MethodDrillFollowUpWeighting.IsStructurallyValid()) { CoachHandoffRecommendationDetailLine += MethodDrillFollowUpWeighting.bPreferDrillFollowUp @@ -6942,7 +7074,12 @@ void UHyperTwistCoachDashboardWidget::UpdateDashboardPresentation() ? TEXT("prefer launch-recovery") : TEXT("no upstream preference")); const FString QueueRecoveryMethodDrillStatus = - QueueRecoveryWeighting.bHasMethodDrillOutcomeSignal + QueueRecoveryWeighting.bHasMethodDrillMemorySignal + ? (QueueRecoveryWeighting.PreferredActionKind + == FHyperTwistCoachDashboardQueueRecoveryOutcome::EActionKind::LaunchRecovery + ? TEXT(" | Upstream drill-driven recovery memory is reinforcing launch recovery.") + : TEXT(" | Upstream drill-driven recovery memory is reinforcing promote-deferred recovery.")) + : (QueueRecoveryWeighting.bHasMethodDrillOutcomeSignal ? (QueueRecoveryWeighting.PreferredActionKind == FHyperTwistCoachDashboardQueueRecoveryOutcome::EActionKind::LaunchRecovery ? TEXT(" | Prior drill-weighted recovery outcomes are reinforcing launch recovery.") @@ -6952,7 +7089,7 @@ void UHyperTwistCoachDashboardWidget::UpdateDashboardPresentation() == FHyperTwistCoachDashboardQueueRecoveryOutcome::EActionKind::LaunchRecovery ? TEXT(" | Drill-driven accepted handoff history is reinforcing launch recovery.") : TEXT(" | Drill-driven accepted handoff history is present, but promote-deferred recovery still holds.")) - : TEXT("")); + : TEXT(""))); const bool bHasQueueRecoveryFriction = CachedCoachSchedulePolicySummary.bHasBlockedFollowUpDeferrals || CachedCoachSchedulePolicySummary.bHasArchiveAwareDeferrals @@ -7005,7 +7142,7 @@ void UHyperTwistCoachDashboardWidget::UpdateDashboardPresentation() : *QueueDeferredFirstSuppressionSignal->Detail) : TEXT(""); const FString QueueRecoveryDetailLine = FString::Printf( - TEXT("Deferred events %d | Promoted %d | Manual %d | Pressure %.2f | Dominant reason: %s | Follow-up deferrals: %s | Archive-aware deferrals: %s | Completion gap: %s | Upstream action source: %s%s || Learned promote %d/%d reduced, %d failed, avg delta %.2f | launch %d/%d reduced, %d failed, avg delta %.2f | deferred-first promote/launch reduced %d/%d over %d/%d | drill-driven outcome promote/launch reduced %d/%d over %d/%d | drill-driven bias promote/launch %d/%d"), + TEXT("Deferred events %d | Promoted %d | Manual %d | Pressure %.2f | Dominant reason: %s | Follow-up deferrals: %s | Archive-aware deferrals: %s | Completion gap: %s | Upstream action source: %s%s || Learned promote %d/%d reduced, %d failed, avg delta %.2f | launch %d/%d reduced, %d failed, avg delta %.2f | deferred-first promote/launch reduced %d/%d over %d/%d | drill-driven outcome promote/launch reduced %d/%d over %d/%d | drill-driven bias promote/launch %d/%d | upstream drill recovery strong/weak/history %d/%d/%d avg %.1f"), CachedCoachQueueExecutionSummary.DeferredEventCount, CachedCoachQueueExecutionSummary.PromotedEventCount, CachedCoachQueueExecutionSummary.ManualRescheduleEventCount, @@ -7035,7 +7172,11 @@ void UHyperTwistCoachDashboardWidget::UpdateDashboardPresentation() QueueRecoveryWeighting.MethodDrillPromoteCount, QueueRecoveryWeighting.MethodDrillLaunchCount, QueueRecoveryWeighting.MethodDrillPromoteBiasCount, - QueueRecoveryWeighting.MethodDrillLaunchBiasCount + QueueRecoveryWeighting.MethodDrillLaunchBiasCount, + QueueRecoveryWeighting.MethodDrillUpstreamStrongCount, + QueueRecoveryWeighting.MethodDrillUpstreamWeakCount, + QueueRecoveryWeighting.MethodDrillUpstreamHistoryCount, + QueueRecoveryWeighting.MethodDrillUpstreamAverageOutcomeScore ); const bool bHasPrimaryQueueSuppression = CachedCoachSessionQueueSummary.bSuppressedPrimaryQueueCreation diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistCoachDashboardWidget.h b/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistCoachDashboardWidget.h index 4939265..5dc142b 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistCoachDashboardWidget.h +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistCoachDashboardWidget.h @@ -176,14 +176,19 @@ struct FHyperTwistCoachDashboardQueueRecoveryWeighting int32 MethodDrillLaunchFailureCount = 0; int32 MethodDrillPromoteBiasCount = 0; int32 MethodDrillLaunchBiasCount = 0; + int32 MethodDrillUpstreamHistoryCount = 0; + int32 MethodDrillUpstreamStrongCount = 0; + int32 MethodDrillUpstreamWeakCount = 0; float PromoteAveragePressureDelta = 0.0f; float LaunchAveragePressureDelta = 0.0f; + float MethodDrillUpstreamAverageOutcomeScore = 0.0f; FHyperTwistCoachDashboardQueueRecoveryOutcome::EActionKind PreferredActionKind = FHyperTwistCoachDashboardQueueRecoveryOutcome::EActionKind::None; bool bHasSignal = false; bool bHasDeferredFirstSignal = false; bool bHasMethodDrillSignal = false; bool bHasMethodDrillOutcomeSignal = false; + bool bHasMethodDrillMemorySignal = false; bool IsStructurallyValid() const {