Weight queue recovery by drill-driven handoff history

This commit is contained in:
axiomlogicnexus 2026-04-29 03:46:14 +02:00
parent 80ddba6946
commit fc7c487fca
2 changed files with 127 additions and 42 deletions

View file

@ -750,6 +750,16 @@ namespace HyperTwistCoachDashboardWidgetInternal
Bias.bHasSignal = Bias.PreferredKind != EHandoffRecommendationKind::None;
return Bias;
}
int32 DeriveMethodDrillLaunchRecoveryBias(const FHandoffHistoryDashboardBias& Bias)
{
return Bias.MethodDrillQueueAcceptedDeltaCount * 2
+ Bias.MethodDrillFollowUpAcceptedDeltaCount * 3
+ Bias.MethodDrillRecommendedAcceptedDeltaCount
+ Bias.MethodDrillQueueRecommendationCount
+ Bias.MethodDrillFollowUpRecommendationCount * 2
+ Bias.MethodDrillRecommendedRecommendationCount;
}
}
void UHyperTwistCoachDashboardWidget::NativeConstruct()
@ -911,6 +921,14 @@ bool UHyperTwistCoachDashboardWidget::ApplyQueueRecoveryPosture()
&& (QueueRecoveryWeighting.PreferredActionKind
== FHyperTwistCoachDashboardQueueRecoveryOutcome::EActionKind::LaunchRecovery
|| (!QueueRecoveryWeighting.IsStructurallyValid() && bUpstreamPrefersLaunchRecovery));
const bool bUseMethodDrillRecoverySignal =
!bHasDeferredFirstSuppressionSignal
&& QueueRecoveryWeighting.bHasMethodDrillSignal
&& QueueRecoveryWeighting.MethodDrillLaunchBiasCount
>= QueueRecoveryWeighting.MethodDrillPromoteBiasCount
&& (QueueRecoveryWeighting.PreferredActionKind
== FHyperTwistCoachDashboardQueueRecoveryOutcome::EActionKind::LaunchRecovery
|| (!QueueRecoveryWeighting.IsStructurallyValid() && bUpstreamPrefersLaunchRecovery));
auto StartRunAndRefresh = [this](const FHyperTwistTrainingRunState& RunState)
{
@ -929,7 +947,8 @@ bool UHyperTwistCoachDashboardWidget::ApplyQueueRecoveryPosture()
bBeforeOutstandingDeferred,
bBeforeCompletionGap,
bHasDeferredFirstSuppressionSignal,
bPrefersFollowUp
bPrefersFollowUp,
bUseMethodDrillRecoverySignal
](
const TCHAR* AppliedActionLabel,
const FHyperTwistCoachDashboardQueueRecoveryOutcome::EActionKind ActionKind,
@ -945,11 +964,34 @@ bool UHyperTwistCoachDashboardWidget::ApplyQueueRecoveryPosture()
bBeforeOutstandingDeferred,
bBeforeCompletionGap,
bHasDeferredFirstSuppressionSignal && bPrefersFollowUp,
bUseMethodDrillRecoverySignal,
bSucceeded
);
UpdateDashboardPresentation();
return bSucceeded;
};
auto BuildRecoveryLaunchSourceLabel =
[bHasDeferredFirstSuppressionSignal, bUseMethodDrillRecoverySignal](
const EHyperTwistTrainingCoachHandoffKind HandoffKind)
{
if (bHasDeferredFirstSuppressionSignal)
{
return HyperTwistCoachDashboardWidgetInternal::DashboardHandoffSourceLabel(
HandoffKind,
true
);
}
if (bUseMethodDrillRecoverySignal)
{
return HyperTwistCoachDashboardWidgetInternal::DashboardHandoffSourceLabel(
HandoffKind,
false,
true
);
}
return FString();
};
auto TryPromoteRecovery = [&]() -> bool
{
if (DeferredEntryId.IsEmpty())
@ -981,7 +1023,8 @@ bool UHyperTwistCoachDashboardWidget::ApplyQueueRecoveryPosture()
? HyperTwistCoachDashboardWidgetInternal::DashboardHandoffSourceLabel(
EHyperTwistTrainingCoachHandoffKind::Queue,
true)
: FString();
: BuildRecoveryLaunchSourceLabel(
EHyperTwistTrainingCoachHandoffKind::Queue);
const bool bSucceeded = StartRunAndRefresh(
StartNextQueuedCoachRun(DefaultCoachMaxCases, StartActionSourceLabel));
return RecordAndFinalizeOutcome(
@ -996,12 +1039,9 @@ bool UHyperTwistCoachDashboardWidget::ApplyQueueRecoveryPosture()
&& CachedCoachPanelState.bCanStartCoachFollowUpRun
&& CachedCoachPanelState.bHasFollowUpGuidance)
{
const FString StartActionSourceLabel =
bHasDeferredFirstSuppressionSignal
? HyperTwistCoachDashboardWidgetInternal::DashboardHandoffSourceLabel(
EHyperTwistTrainingCoachHandoffKind::FollowUp,
true)
: FString();
const FString StartActionSourceLabel = BuildRecoveryLaunchSourceLabel(
EHyperTwistTrainingCoachHandoffKind::FollowUp
);
const bool bSucceeded = StartRunAndRefresh(
StartCoachFollowUpRun(DefaultCoachMaxCases, StartActionSourceLabel));
return RecordAndFinalizeOutcome(
@ -1012,12 +1052,9 @@ bool UHyperTwistCoachDashboardWidget::ApplyQueueRecoveryPosture()
}
if (bPrefersRecommended && CachedCoachPanelState.bCanStartCoachRecommendedRun)
{
const FString StartActionSourceLabel =
bHasDeferredFirstSuppressionSignal
? HyperTwistCoachDashboardWidgetInternal::DashboardHandoffSourceLabel(
EHyperTwistTrainingCoachHandoffKind::Recommended,
true)
: FString();
const FString StartActionSourceLabel = BuildRecoveryLaunchSourceLabel(
EHyperTwistTrainingCoachHandoffKind::Recommended
);
const bool bSucceeded = StartRunAndRefresh(
StartCoachRecommendedRun(DefaultCoachMaxCases, StartActionSourceLabel));
return RecordAndFinalizeOutcome(
@ -1033,7 +1070,8 @@ bool UHyperTwistCoachDashboardWidget::ApplyQueueRecoveryPosture()
? HyperTwistCoachDashboardWidgetInternal::DashboardHandoffSourceLabel(
EHyperTwistTrainingCoachHandoffKind::Queue,
true)
: FString();
: BuildRecoveryLaunchSourceLabel(
EHyperTwistTrainingCoachHandoffKind::Queue);
const bool bSucceeded = StartRunAndRefresh(
StartNextQueuedCoachRun(DefaultCoachMaxCases, StartActionSourceLabel));
return RecordAndFinalizeOutcome(
@ -1044,12 +1082,9 @@ bool UHyperTwistCoachDashboardWidget::ApplyQueueRecoveryPosture()
}
if (CachedCoachPanelState.bCanStartCoachFollowUpRun && CachedCoachPanelState.bHasFollowUpGuidance)
{
const FString StartActionSourceLabel =
bHasDeferredFirstSuppressionSignal
? HyperTwistCoachDashboardWidgetInternal::DashboardHandoffSourceLabel(
EHyperTwistTrainingCoachHandoffKind::FollowUp,
true)
: FString();
const FString StartActionSourceLabel = BuildRecoveryLaunchSourceLabel(
EHyperTwistTrainingCoachHandoffKind::FollowUp
);
const bool bSucceeded = StartRunAndRefresh(
StartCoachFollowUpRun(DefaultCoachMaxCases, StartActionSourceLabel));
return RecordAndFinalizeOutcome(
@ -1060,12 +1095,9 @@ bool UHyperTwistCoachDashboardWidget::ApplyQueueRecoveryPosture()
}
if (CachedCoachPanelState.bCanStartCoachRecommendedRun)
{
const FString StartActionSourceLabel =
bHasDeferredFirstSuppressionSignal
? HyperTwistCoachDashboardWidgetInternal::DashboardHandoffSourceLabel(
EHyperTwistTrainingCoachHandoffKind::Recommended,
true)
: FString();
const FString StartActionSourceLabel = BuildRecoveryLaunchSourceLabel(
EHyperTwistTrainingCoachHandoffKind::Recommended
);
const bool bSucceeded = StartRunAndRefresh(
StartCoachRecommendedRun(DefaultCoachMaxCases, StartActionSourceLabel));
return RecordAndFinalizeOutcome(
@ -4619,6 +4651,10 @@ FHyperTwistCoachDashboardQueueRecoveryWeighting UHyperTwistCoachDashboardWidget:
FHyperTwistCoachDashboardQueueRecoveryWeighting Weighting;
float PromoteDeltaAccumulator = 0.0f;
float LaunchDeltaAccumulator = 0.0f;
const HyperTwistCoachDashboardWidgetInternal::FHandoffHistoryDashboardBias HandoffHistoryBias =
HyperTwistCoachDashboardWidgetInternal::DeriveHandoffHistoryDashboardBias(
HandoffHistoryEntries
);
auto ConsumeOutcome = [
&Weighting,
@ -4709,17 +4745,40 @@ FHyperTwistCoachDashboardQueueRecoveryWeighting UHyperTwistCoachDashboardWidget:
LaunchDeltaAccumulator / static_cast<float>(Weighting.LaunchCount);
}
Weighting.bHasSignal = Weighting.PromoteCount > 0 || Weighting.LaunchCount > 0;
Weighting.MethodDrillLaunchBiasCount =
HyperTwistCoachDashboardWidgetInternal::DeriveMethodDrillLaunchRecoveryBias(
HandoffHistoryBias
);
if (CachedCoachPanelState.PreferredGuidanceSourceLabel
== TEXT("coach-memory-method-drill-follow-up-strong"))
{
Weighting.MethodDrillLaunchBiasCount += 2;
}
else if (
CachedCoachPanelState.PreferredGuidanceSourceLabel
== TEXT("coach-memory-method-drill-follow-up-weak"))
{
Weighting.MethodDrillPromoteBiasCount += 2;
}
Weighting.bHasMethodDrillSignal =
Weighting.MethodDrillLaunchBiasCount > 0 || Weighting.MethodDrillPromoteBiasCount > 0;
const bool bHasDirectOutcomeSignal = Weighting.PromoteCount > 0 || Weighting.LaunchCount > 0;
Weighting.bHasSignal = bHasDirectOutcomeSignal || Weighting.bHasMethodDrillSignal;
if (!Weighting.bHasSignal)
{
return Weighting;
}
auto ScoreAction = [](const int32 Count, const int32 ReducedCount, const int32 FailureCount, const float AverageDelta)
auto ScoreAction = [bHasDirectOutcomeSignal](
const int32 Count,
const int32 ReducedCount,
const int32 FailureCount,
const float AverageDelta)
{
if (Count <= 0)
{
return -1000000.0f;
return bHasDirectOutcomeSignal ? -1000000.0f : 0.0f;
}
const int32 NeutralCount = FMath::Max(Count - ReducedCount - FailureCount, 0);
@ -4756,9 +4815,13 @@ FHyperTwistCoachDashboardQueueRecoveryWeighting UHyperTwistCoachDashboardWidget:
0))
* 0.5f;
const float AdjustedPromoteScore =
PromoteScore + (Weighting.bHasDeferredFirstSignal ? DeferredFirstPromoteScore * 1.5f : 0.0f);
PromoteScore
+ (Weighting.bHasDeferredFirstSignal ? DeferredFirstPromoteScore * 1.5f : 0.0f)
+ static_cast<float>(Weighting.MethodDrillPromoteBiasCount) * 1.5f;
const float AdjustedLaunchScore =
LaunchScore + (Weighting.bHasDeferredFirstSignal ? DeferredFirstLaunchScore * 0.75f : 0.0f);
LaunchScore
+ (Weighting.bHasDeferredFirstSignal ? DeferredFirstLaunchScore * 0.75f : 0.0f)
+ static_cast<float>(Weighting.MethodDrillLaunchBiasCount) * 0.85f;
if (AdjustedPromoteScore > AdjustedLaunchScore)
{
@ -5001,6 +5064,7 @@ void UHyperTwistCoachDashboardWidget::RecordQueueRecoveryOutcome(
const bool bBeforeOutstandingDeferred,
const bool bBeforeCompletionGap,
const bool bUsedDeferredFirstSuppressionSignal,
const bool bUsedMethodDrillHandoffSignal,
const bool bSucceeded)
{
QueueRecoveryOutcome.RecordedAtUtc = FDateTime::UtcNow().ToIso8601();
@ -5035,6 +5099,8 @@ void UHyperTwistCoachDashboardWidget::RecordQueueRecoveryOutcome(
);
QueueRecoveryOutcome.bUsedDeferredFirstSuppressionSignal =
bUsedDeferredFirstSuppressionSignal;
QueueRecoveryOutcome.bUsedMethodDrillHandoffSignal =
bUsedMethodDrillHandoffSignal;
bHasQueueRecoveryOutcome = QueueRecoveryOutcome.IsStructurallyValid();
if (bHasQueueRecoveryOutcome)
{
@ -6635,6 +6701,13 @@ void UHyperTwistCoachDashboardWidget::UpdateDashboardPresentation()
: (bUpstreamPrefersLaunchRecovery
? TEXT("prefer launch-recovery")
: TEXT("no upstream preference"));
const FString QueueRecoveryMethodDrillStatus =
QueueRecoveryWeighting.bHasMethodDrillSignal
? (QueueRecoveryWeighting.PreferredActionKind
== 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("");
const bool bHasQueueRecoveryFriction =
CachedCoachSchedulePolicySummary.bHasBlockedFollowUpDeferrals
|| CachedCoachSchedulePolicySummary.bHasArchiveAwareDeferrals
@ -6643,7 +6716,7 @@ void UHyperTwistCoachDashboardWidget::UpdateDashboardPresentation()
|| CachedCoachQueueExecutionSummary.bHasCompletionGap;
const FString QueueRecoveryStatusLine = bQueueRecoveryMemorySource
? FString::Printf(
TEXT("Recovery: %s lane | Source: %s | Action: %s | Mode: %s | Selection: %s | Upstream: %s | Learned: %s%s"),
TEXT("Recovery: %s lane | Source: %s | Action: %s | Mode: %s | Selection: %s | Upstream: %s | Learned: %s%s%s"),
*QueueRecoveryLaneLabel,
*QueueRecoverySourceLabel,
CachedCoachPanelState.PrimaryActionLabel.IsEmpty()
@ -6655,24 +6728,27 @@ void UHyperTwistCoachDashboardWidget::UpdateDashboardPresentation()
*QueueRecoveryWeightingLabel,
bHasDeferredFirstQueueSuppressionSignal && bQueueRecoveryFollowUp
? TEXT(" | Deferred-first suppression is keeping narrowed follow-up recovery primary.")
: TEXT("")
: TEXT(""),
*QueueRecoveryMethodDrillStatus
)
: (bHasQueueRecoveryFriction
? FString::Printf(
TEXT("Recovery: queue friction is present, but no queue-memory recovery override is currently active | Upstream: %s | Learned: %s%s"),
TEXT("Recovery: queue friction is present, but no queue-memory recovery override is currently active | Upstream: %s | Learned: %s%s%s"),
*QueueRecoveryUpstreamActionLabel,
*QueueRecoveryWeightingLabel,
bHasDeferredFirstQueueSuppressionSignal
? TEXT(" | Deferred-first suppression is still visible in the live coaching signal.")
: TEXT("")
: TEXT(""),
*QueueRecoveryMethodDrillStatus
)
: FString::Printf(
TEXT("Recovery: no queue-memory recovery override is currently active | Upstream: %s | Learned: %s%s"),
TEXT("Recovery: no queue-memory recovery override is currently active | Upstream: %s | Learned: %s%s%s"),
*QueueRecoveryUpstreamActionLabel,
*QueueRecoveryWeightingLabel,
bHasDeferredFirstQueueSuppressionSignal
? TEXT(" | Deferred-first suppression is still visible in the live coaching signal.")
: TEXT("")
: TEXT(""),
*QueueRecoveryMethodDrillStatus
));
const FString QueueRecoveryDeferredFirstDetail =
bHasDeferredFirstQueueSuppressionSignal
@ -6684,7 +6760,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"),
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 promote/launch bias %d/%d"),
CachedCoachQueueExecutionSummary.DeferredEventCount,
CachedCoachQueueExecutionSummary.PromotedEventCount,
CachedCoachQueueExecutionSummary.ManualRescheduleEventCount,
@ -6708,7 +6784,9 @@ void UHyperTwistCoachDashboardWidget::UpdateDashboardPresentation()
QueueRecoveryWeighting.DeferredFirstPromoteReducedCount,
QueueRecoveryWeighting.DeferredFirstLaunchReducedCount,
QueueRecoveryWeighting.DeferredFirstPromoteCount,
QueueRecoveryWeighting.DeferredFirstLaunchCount
QueueRecoveryWeighting.DeferredFirstLaunchCount,
QueueRecoveryWeighting.MethodDrillPromoteBiasCount,
QueueRecoveryWeighting.MethodDrillLaunchBiasCount
);
const bool bHasPrimaryQueueSuppression =
CachedCoachSessionQueueSummary.bSuppressedPrimaryQueueCreation
@ -6809,7 +6887,7 @@ void UHyperTwistCoachDashboardWidget::UpdateDashboardPresentation()
const FString QueueRecoveryOutcomeDetailLine = !bHasQueueRecoveryOutcome || !QueueRecoveryOutcome.IsStructurallyValid()
? TEXT("Outcome detail: apply a queue-recovery action to compare before/after pressure, deferred count, blocked follow-up state, and completion-gap state.")
: FString::Printf(
TEXT("Recorded: %s | Pressure %.2f -> %.2f | Deferred %d -> %d | Blocked follow-up %s -> %s | Outstanding deferred %s -> %s | Completion gap %s -> %s"),
TEXT("Recorded: %s | Pressure %.2f -> %.2f | Deferred %d -> %d | Blocked follow-up %s -> %s | Outstanding deferred %s -> %s | Completion gap %s -> %s | deferred-first weighted: %s | drill-weighted: %s"),
*QueueRecoveryOutcome.RecordedAtUtc,
QueueRecoveryOutcome.BeforePressureScore,
QueueRecoveryOutcome.AfterPressureScore,
@ -6820,7 +6898,9 @@ void UHyperTwistCoachDashboardWidget::UpdateDashboardPresentation()
QueueRecoveryOutcome.bBeforeOutstandingDeferred ? TEXT("yes") : TEXT("no"),
QueueRecoveryOutcome.bAfterOutstandingDeferred ? TEXT("yes") : TEXT("no"),
QueueRecoveryOutcome.bBeforeCompletionGap ? TEXT("yes") : TEXT("no"),
QueueRecoveryOutcome.bAfterCompletionGap ? TEXT("yes") : TEXT("no")
QueueRecoveryOutcome.bAfterCompletionGap ? TEXT("yes") : TEXT("no"),
QueueRecoveryOutcome.bUsedDeferredFirstSuppressionSignal ? TEXT("yes") : TEXT("no"),
QueueRecoveryOutcome.bUsedMethodDrillHandoffSignal ? TEXT("yes") : TEXT("no")
);
const FString PreferredDeferredRecoveryEntryId = FindFirstDeferredQueueEntryId();
const bool bCanApplyQueueRecovery =

View file

@ -148,6 +148,7 @@ struct FHyperTwistCoachDashboardQueueRecoveryOutcome
bool bReducedFriction = false;
bool bShiftedFriction = false;
bool bUsedDeferredFirstSuppressionSignal = false;
bool bUsedMethodDrillHandoffSignal = false;
bool IsStructurallyValid() const
{
@ -167,12 +168,15 @@ struct FHyperTwistCoachDashboardQueueRecoveryWeighting
int32 DeferredFirstPromoteReducedCount = 0;
int32 DeferredFirstLaunchCount = 0;
int32 DeferredFirstLaunchReducedCount = 0;
int32 MethodDrillPromoteBiasCount = 0;
int32 MethodDrillLaunchBiasCount = 0;
float PromoteAveragePressureDelta = 0.0f;
float LaunchAveragePressureDelta = 0.0f;
FHyperTwistCoachDashboardQueueRecoveryOutcome::EActionKind PreferredActionKind =
FHyperTwistCoachDashboardQueueRecoveryOutcome::EActionKind::None;
bool bHasSignal = false;
bool bHasDeferredFirstSignal = false;
bool bHasMethodDrillSignal = false;
bool IsStructurallyValid() const
{
@ -1241,6 +1245,7 @@ private:
bool bBeforeOutstandingDeferred,
bool bBeforeCompletionGap,
bool bUsedDeferredFirstSuppressionSignal,
bool bUsedMethodDrillHandoffSignal,
bool bSucceeded
);
void RecordCoachHandoffHistorySnapshot(