From af9dc0c2eb9988ca94c3f1045c438248567c4a22 Mon Sep 17 00:00:00 2001 From: axiomlogicnexus Date: Tue, 28 Apr 2026 03:22:28 +0200 Subject: [PATCH] Retain deferred-first dashboard learning --- .../HyperTwistCoachDashboardWidget.cpp | 97 +++++++++++++++++-- .../HyperTwistCoachDashboardWidget.h | 11 ++- 2 files changed, 97 insertions(+), 11 deletions(-) diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistCoachDashboardWidget.cpp b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistCoachDashboardWidget.cpp index 11f74f6..383fa28 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistCoachDashboardWidget.cpp +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistCoachDashboardWidget.cpp @@ -39,6 +39,9 @@ namespace HyperTwistCoachDashboardWidgetInternal int32 QueueRecommendationCount = 0; int32 FollowUpRecommendationCount = 0; int32 RecommendedRecommendationCount = 0; + int32 DeferredFirstQueueRecommendationCount = 0; + int32 DeferredFirstFollowUpRecommendationCount = 0; + int32 DeferredFirstRecommendedRecommendationCount = 0; int32 QueueScore = 0; int32 FollowUpScore = 0; int32 RecommendedScore = 0; @@ -47,6 +50,7 @@ namespace HyperTwistCoachDashboardWidgetInternal bool bHasAcceptedSignal = false; bool bHasRecommendationSignal = false; bool bHasDeferredOverflowAcceptedSignal = false; + bool bHasDeferredFirstSuppressionRecommendationSignal = false; bool bHasSignal = false; }; @@ -487,18 +491,36 @@ namespace HyperTwistCoachDashboardWidgetInternal ++Bias.QueueRecommendationCount; ++Bias.QueueScore; Bias.bHasRecommendationSignal = true; + if (Entry.bUsedDeferredFirstSuppressionSignal) + { + ++Bias.DeferredFirstQueueRecommendationCount; + Bias.QueueScore += 3; + Bias.bHasDeferredFirstSuppressionRecommendationSignal = true; + } } else if (Entry.RecommendedHandoffKindLabel.Equals(TEXT("follow-up"), ESearchCase::IgnoreCase)) { ++Bias.FollowUpRecommendationCount; ++Bias.FollowUpScore; Bias.bHasRecommendationSignal = true; + if (Entry.bUsedDeferredFirstSuppressionSignal) + { + ++Bias.DeferredFirstFollowUpRecommendationCount; + Bias.FollowUpScore += 4; + Bias.bHasDeferredFirstSuppressionRecommendationSignal = true; + } } else if (Entry.RecommendedHandoffKindLabel.Equals(TEXT("recommended"), ESearchCase::IgnoreCase)) { ++Bias.RecommendedRecommendationCount; ++Bias.RecommendedScore; Bias.bHasRecommendationSignal = true; + if (Entry.bUsedDeferredFirstSuppressionSignal) + { + ++Bias.DeferredFirstRecommendedRecommendationCount; + ++Bias.RecommendedScore; + Bias.bHasDeferredFirstSuppressionRecommendationSignal = true; + } } PreviousEntry = Entry; @@ -744,7 +766,9 @@ bool UHyperTwistCoachDashboardWidget::ApplyQueueRecoveryPosture() BeforeDeferredEventCount, bBeforeBlockedFollowUp, bBeforeOutstandingDeferred, - bBeforeCompletionGap + bBeforeCompletionGap, + bHasDeferredFirstSuppressionSignal, + bPrefersFollowUp ]( const TCHAR* AppliedActionLabel, const FHyperTwistCoachDashboardQueueRecoveryOutcome::EActionKind ActionKind, @@ -759,6 +783,7 @@ bool UHyperTwistCoachDashboardWidget::ApplyQueueRecoveryPosture() bBeforeBlockedFollowUp, bBeforeOutstandingDeferred, bBeforeCompletionGap, + bHasDeferredFirstSuppressionSignal && bPrefersFollowUp, bSucceeded ); UpdateDashboardPresentation(); @@ -4175,9 +4200,18 @@ FHyperTwistCoachDashboardQueueRecoveryWeighting UHyperTwistCoachDashboardWidget: case FHyperTwistCoachDashboardQueueRecoveryOutcome::EActionKind::PromoteDeferred: ++Weighting.PromoteCount; PromoteDeltaAccumulator += PressureDelta; + if (Outcome.bUsedDeferredFirstSuppressionSignal) + { + ++Weighting.DeferredFirstPromoteCount; + Weighting.bHasDeferredFirstSignal = true; + } if (Outcome.bReducedFriction) { ++Weighting.PromoteReducedCount; + if (Outcome.bUsedDeferredFirstSuppressionSignal) + { + ++Weighting.DeferredFirstPromoteReducedCount; + } } if (!Outcome.bSucceeded) { @@ -4187,9 +4221,18 @@ FHyperTwistCoachDashboardQueueRecoveryWeighting UHyperTwistCoachDashboardWidget: case FHyperTwistCoachDashboardQueueRecoveryOutcome::EActionKind::LaunchRecovery: ++Weighting.LaunchCount; LaunchDeltaAccumulator += PressureDelta; + if (Outcome.bUsedDeferredFirstSuppressionSignal) + { + ++Weighting.DeferredFirstLaunchCount; + Weighting.bHasDeferredFirstSignal = true; + } if (Outcome.bReducedFriction) { ++Weighting.LaunchReducedCount; + if (Outcome.bUsedDeferredFirstSuppressionSignal) + { + ++Weighting.DeferredFirstLaunchReducedCount; + } } if (!Outcome.bSucceeded) { @@ -4261,13 +4304,31 @@ FHyperTwistCoachDashboardQueueRecoveryWeighting UHyperTwistCoachDashboardWidget: Weighting.LaunchFailureCount, Weighting.LaunchAveragePressureDelta ); + const float DeferredFirstPromoteScore = + static_cast(Weighting.DeferredFirstPromoteReducedCount) * 3.0f + + static_cast( + FMath::Max( + Weighting.DeferredFirstPromoteCount - Weighting.DeferredFirstPromoteReducedCount, + 0)) + * 0.5f; + const float DeferredFirstLaunchScore = + static_cast(Weighting.DeferredFirstLaunchReducedCount) * 3.0f + + static_cast( + FMath::Max( + Weighting.DeferredFirstLaunchCount - Weighting.DeferredFirstLaunchReducedCount, + 0)) + * 0.5f; + const float AdjustedPromoteScore = + PromoteScore + (Weighting.bHasDeferredFirstSignal ? DeferredFirstPromoteScore * 1.5f : 0.0f); + const float AdjustedLaunchScore = + LaunchScore + (Weighting.bHasDeferredFirstSignal ? DeferredFirstLaunchScore * 0.75f : 0.0f); - if (PromoteScore > LaunchScore) + if (AdjustedPromoteScore > AdjustedLaunchScore) { Weighting.PreferredActionKind = FHyperTwistCoachDashboardQueueRecoveryOutcome::EActionKind::PromoteDeferred; } - else if (LaunchScore > PromoteScore) + else if (AdjustedLaunchScore > AdjustedPromoteScore) { Weighting.PreferredActionKind = FHyperTwistCoachDashboardQueueRecoveryOutcome::EActionKind::LaunchRecovery; @@ -4434,6 +4495,7 @@ void UHyperTwistCoachDashboardWidget::RecordQueueRecoveryOutcome( const bool bBeforeBlockedFollowUp, const bool bBeforeOutstandingDeferred, const bool bBeforeCompletionGap, + const bool bUsedDeferredFirstSuppressionSignal, const bool bSucceeded) { QueueRecoveryOutcome.RecordedAtUtc = FDateTime::UtcNow().ToIso8601(); @@ -4466,6 +4528,8 @@ void UHyperTwistCoachDashboardWidget::RecordQueueRecoveryOutcome( || QueueRecoveryOutcome.bAfterOutstandingDeferred != QueueRecoveryOutcome.bBeforeOutstandingDeferred || QueueRecoveryOutcome.bAfterCompletionGap != QueueRecoveryOutcome.bBeforeCompletionGap ); + QueueRecoveryOutcome.bUsedDeferredFirstSuppressionSignal = + bUsedDeferredFirstSuppressionSignal; bHasQueueRecoveryOutcome = QueueRecoveryOutcome.IsStructurallyValid(); if (bHasQueueRecoveryOutcome) { @@ -4488,7 +4552,8 @@ void UHyperTwistCoachDashboardWidget::RecordCoachHandoffHistorySnapshot( const int32 ClosureSuppressionBias, const bool bQueueReady, const bool bFollowUpReady, - const bool bRecommendedReady) + const bool bRecommendedReady, + const bool bUsedDeferredFirstSuppressionSignal) { FHyperTwistCoachDashboardHandoffHistoryEntry Entry; Entry.RecordedAtUtc = FDateTime::UtcNow().ToIso8601(); @@ -4519,6 +4584,7 @@ void UHyperTwistCoachDashboardWidget::RecordCoachHandoffHistorySnapshot( Entry.bQueueReady = bQueueReady; Entry.bFollowUpReady = bFollowUpReady; Entry.bRecommendedReady = bRecommendedReady; + Entry.bUsedDeferredFirstSuppressionSignal = bUsedDeferredFirstSuppressionSignal; if (!Entry.IsStructurallyValid()) { return; @@ -4557,7 +4623,9 @@ void UHyperTwistCoachDashboardWidget::RecordCoachHandoffHistorySnapshot( == Entry.ClosureSuppressionBias && HandoffHistoryEntries.Last().bQueueReady == Entry.bQueueReady && HandoffHistoryEntries.Last().bFollowUpReady == Entry.bFollowUpReady - && HandoffHistoryEntries.Last().bRecommendedReady == Entry.bRecommendedReady; + && HandoffHistoryEntries.Last().bRecommendedReady == Entry.bRecommendedReady + && HandoffHistoryEntries.Last().bUsedDeferredFirstSuppressionSignal + == Entry.bUsedDeferredFirstSuppressionSignal; if (bMatchesLastEntry) { return; @@ -5262,7 +5330,7 @@ void UHyperTwistCoachDashboardWidget::UpdateDashboardPresentation() } 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 | 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 | 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 | 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"), @@ -5305,6 +5373,9 @@ void UHyperTwistCoachDashboardWidget::UpdateDashboardPresentation() HandoffHistoryBias.DeferredOverflowQueueAcceptedDeltaCount, HandoffHistoryBias.DeferredOverflowFollowUpAcceptedDeltaCount, HandoffHistoryBias.DeferredOverflowRecommendedAcceptedDeltaCount, + HandoffHistoryBias.DeferredFirstQueueRecommendationCount, + HandoffHistoryBias.DeferredFirstFollowUpRecommendationCount, + HandoffHistoryBias.DeferredFirstRecommendedRecommendationCount, HandoffHistoryBias.QueueRecommendationCount, HandoffHistoryBias.FollowUpRecommendationCount, HandoffHistoryBias.RecommendedRecommendationCount @@ -5320,7 +5391,8 @@ void UHyperTwistCoachDashboardWidget::UpdateDashboardPresentation() HandoffClosureBias.SuppressionBias, bQueueReadyForHandoff, bFollowUpReadyForHandoff, - bRecommendedReadyForHandoff + bRecommendedReadyForHandoff, + bHandoffRecommendationUsedDeferredFirstSuppressionSignal ); FString QueuedRunLabel = CachedCoachPanelState.NextQueueSourceLabel.IsEmpty() ? TEXT("Start Queue") @@ -5366,13 +5438,14 @@ void UHyperTwistCoachDashboardWidget::UpdateDashboardPresentation() const FString HandoffHistoryDetailLine = SelectedHandoffHistoryEntry != nullptr ? FString::Printf( - TEXT("Accepted queued/follow-up/recommended: %d/%d/%d | deferred-overflow queued/follow-up/recommended: %d/%d/%d | queue source: %s | preferred lane: %s | memory source: %s | recovery action: %s | recovery source: %s | closure continuation bias %d | closure suppression bias %d | readiness queue/follow-up/recommended: %s/%s/%s || %s"), + TEXT("Accepted queued/follow-up/recommended: %d/%d/%d | deferred-overflow queued/follow-up/recommended: %d/%d/%d | deferred-first suppression recommendation used: %s | queue source: %s | preferred lane: %s | memory source: %s | recovery action: %s | recovery source: %s | closure continuation bias %d | closure suppression bias %d | readiness queue/follow-up/recommended: %s/%s/%s || %s"), SelectedHandoffHistoryEntry->AcceptedQueuedHandoffCount, SelectedHandoffHistoryEntry->AcceptedFollowUpHandoffCount, SelectedHandoffHistoryEntry->AcceptedRecommendedHandoffCount, SelectedHandoffHistoryEntry->AcceptedDeferredOverflowQueuedHandoffCount, SelectedHandoffHistoryEntry->AcceptedDeferredOverflowFollowUpHandoffCount, SelectedHandoffHistoryEntry->AcceptedDeferredOverflowRecommendedHandoffCount, + SelectedHandoffHistoryEntry->bUsedDeferredFirstSuppressionSignal ? TEXT("yes") : TEXT("no"), SelectedHandoffHistoryEntry->QueueSourceLabel.IsEmpty() ? TEXT("n/a") : *SelectedHandoffHistoryEntry->QueueSourceLabel, @@ -5624,7 +5697,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"), + 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"), CachedCoachQueueExecutionSummary.DeferredEventCount, CachedCoachQueueExecutionSummary.PromotedEventCount, CachedCoachQueueExecutionSummary.ManualRescheduleEventCount, @@ -5644,7 +5717,11 @@ void UHyperTwistCoachDashboardWidget::UpdateDashboardPresentation() QueueRecoveryWeighting.LaunchReducedCount, QueueRecoveryWeighting.LaunchCount, QueueRecoveryWeighting.LaunchFailureCount, - QueueRecoveryWeighting.LaunchAveragePressureDelta + QueueRecoveryWeighting.LaunchAveragePressureDelta, + QueueRecoveryWeighting.DeferredFirstPromoteReducedCount, + QueueRecoveryWeighting.DeferredFirstLaunchReducedCount, + QueueRecoveryWeighting.DeferredFirstPromoteCount, + QueueRecoveryWeighting.DeferredFirstLaunchCount ); const bool bHasPrimaryQueueSuppression = CachedCoachSessionQueueSummary.bSuppressedPrimaryQueueCreation diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistCoachDashboardWidget.h b/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistCoachDashboardWidget.h index ffc5566..3e9c6ef 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistCoachDashboardWidget.h +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistCoachDashboardWidget.h @@ -147,6 +147,7 @@ struct FHyperTwistCoachDashboardQueueRecoveryOutcome bool bSucceeded = false; bool bReducedFriction = false; bool bShiftedFriction = false; + bool bUsedDeferredFirstSuppressionSignal = false; bool IsStructurallyValid() const { @@ -162,11 +163,16 @@ struct FHyperTwistCoachDashboardQueueRecoveryWeighting int32 LaunchCount = 0; int32 LaunchReducedCount = 0; int32 LaunchFailureCount = 0; + int32 DeferredFirstPromoteCount = 0; + int32 DeferredFirstPromoteReducedCount = 0; + int32 DeferredFirstLaunchCount = 0; + int32 DeferredFirstLaunchReducedCount = 0; float PromoteAveragePressureDelta = 0.0f; float LaunchAveragePressureDelta = 0.0f; FHyperTwistCoachDashboardQueueRecoveryOutcome::EActionKind PreferredActionKind = FHyperTwistCoachDashboardQueueRecoveryOutcome::EActionKind::None; bool bHasSignal = false; + bool bHasDeferredFirstSignal = false; bool IsStructurallyValid() const { @@ -251,6 +257,7 @@ struct FHyperTwistCoachDashboardHandoffHistoryEntry bool bQueueReady = false; bool bFollowUpReady = false; bool bRecommendedReady = false; + bool bUsedDeferredFirstSuppressionSignal = false; bool IsStructurallyValid() const { @@ -1120,6 +1127,7 @@ private: bool bBeforeBlockedFollowUp, bool bBeforeOutstandingDeferred, bool bBeforeCompletionGap, + bool bUsedDeferredFirstSuppressionSignal, bool bSucceeded ); void RecordCoachHandoffHistorySnapshot( @@ -1131,7 +1139,8 @@ private: int32 ClosureSuppressionBias, bool bQueueReady, bool bFollowUpReady, - bool bRecommendedReady + bool bRecommendedReady, + bool bUsedDeferredFirstSuppressionSignal ); void RecordQueueSuppressionSnapshot(); void RecordClosureRecoverySnapshot();