Explain coach handoff memory in UI

This commit is contained in:
axiomlogicnexus 2026-04-27 22:06:26 +02:00
parent a6288a882a
commit d0a55eca5a
2 changed files with 184 additions and 37 deletions

View file

@ -39,6 +39,55 @@ namespace HyperTwistCoachDashboardWidgetInternal
return TEXT("Unknown");
}
bool IsDashboardHandoffGuidanceSource(const FString& SourceLabel)
{
return SourceLabel.StartsWith(TEXT("coach-memory-dashboard-"));
}
bool IsQueueRecoveryMemorySource(const FString& SourceLabel)
{
return SourceLabel.StartsWith(TEXT("coach-memory-queue-"))
|| IsDashboardHandoffGuidanceSource(SourceLabel);
}
FString DescribeCoachMemorySourceLabel(const FString& SourceLabel)
{
if (SourceLabel == TEXT("coach-memory-dashboard-follow-up"))
{
return TEXT("accepted follow-up handoff history");
}
if (SourceLabel == TEXT("coach-memory-dashboard-recommended"))
{
return TEXT("accepted recommended handoff history");
}
if (SourceLabel == TEXT("coach-memory-queue-follow-up"))
{
return TEXT("queue follow-up recovery memory");
}
if (SourceLabel == TEXT("coach-memory-queue-recommended"))
{
return TEXT("queue recommended recovery memory");
}
if (SourceLabel == TEXT("dashboard-handoff-queued"))
{
return TEXT("accepted queued dashboard handoff");
}
if (SourceLabel == TEXT("queue-history-promote-pressure"))
{
return TEXT("queue promote-pressure history");
}
if (SourceLabel == TEXT("queue-history-launch-pressure"))
{
return TEXT("queue launch-pressure history");
}
if (SourceLabel == TEXT("queue-history-balanced-pressure"))
{
return TEXT("balanced queue-recovery history");
}
return SourceLabel.IsEmpty() ? TEXT("n/a") : SourceLabel;
}
UTextBlock* AddTextRow(
UWidgetTree* WidgetTree,
UVerticalBox* Parent,
@ -333,7 +382,8 @@ bool UHyperTwistCoachDashboardWidget::ApplyQueueRecoveryPosture()
const FString DeferredEntryId = FindFirstDeferredQueueEntryId();
const bool bQueueRecoveryMemorySource =
CachedCoachPanelState.PreferredGuidanceSourceLabel.StartsWith(TEXT("coach-memory-queue-"));
HyperTwistCoachDashboardWidgetInternal::IsQueueRecoveryMemorySource(
CachedCoachPanelState.PreferredGuidanceSourceLabel);
const bool bPrefersFollowUp =
CachedCoachPanelState.PreferredGuidanceLane == EHyperTwistTrainingCoachGuidanceLane::FollowUp;
const bool bPrefersRecommended =
@ -4301,6 +4351,12 @@ void UHyperTwistCoachDashboardWidget::UpdateDashboardPresentation()
HyperTwistCoachDashboardWidgetInternal::DeriveClosureRecoveryDashboardBias(
ClosureRecoveryHistoryEntries
);
const FString HandoffMemorySourceLabel =
HyperTwistCoachDashboardWidgetInternal::DescribeCoachMemorySourceLabel(
CachedCoachPanelState.PreferredGuidanceSourceLabel);
const bool bDashboardHandoffMemorySource =
HyperTwistCoachDashboardWidgetInternal::IsDashboardHandoffGuidanceSource(
CachedCoachPanelState.PreferredGuidanceSourceLabel);
const bool bQueueLooksFollowUpOrRecovery =
CachedCoachPanelState.NextQueueSourceLabel.Contains(TEXT("follow-up"))
|| CachedCoachPanelState.NextQueueSourceLabel.Contains(TEXT("queue-recovery"))
@ -4402,7 +4458,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"),
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"),
HandoffClosureBias.ContinuationBias,
HandoffClosureBias.SuppressionBias,
CachedCoachPanelState.bCanStartQueuedRun ? TEXT("yes") : TEXT("no"),
@ -4415,7 +4471,11 @@ void UHyperTwistCoachDashboardWidget::UpdateDashboardPresentation()
: (CachedCoachPanelState.PreferredGuidanceLane
== EHyperTwistTrainingCoachGuidanceLane::Recommended
? TEXT("recommended")
: TEXT("none"))
: TEXT("none")),
*HandoffMemorySourceLabel,
bDashboardHandoffMemorySource
? TEXT(" | Accepted dashboard handoffs are actively reinforcing this recommendation.")
: TEXT("")
);
}
FString QueuedRunLabel = CachedCoachPanelState.NextQueueSourceLabel.IsEmpty()
@ -4524,7 +4584,8 @@ void UHyperTwistCoachDashboardWidget::UpdateDashboardPresentation()
? TEXT("closure-history + memory")
: (CachedCoachPanelState.PreferredGuidanceSourceLabel.IsEmpty()
? TEXT("availability fallback")
: CachedCoachPanelState.PreferredGuidanceSourceLabel));
: HyperTwistCoachDashboardWidgetInternal::DescribeCoachMemorySourceLabel(
CachedCoachPanelState.PreferredGuidanceSourceLabel)));
const FString GuidancePreferenceStatusLine =
GuidanceLanePreference.IsStructurallyValid()
? FString::Printf(
@ -4549,7 +4610,8 @@ void UHyperTwistCoachDashboardWidget::UpdateDashboardPresentation()
)
: TEXT("Preference detail: build recommended or follow-up guidance to let the dashboard choose a next surfaced lane.");
const bool bQueueRecoveryMemorySource =
CachedCoachPanelState.PreferredGuidanceSourceLabel.StartsWith(TEXT("coach-memory-queue-"));
HyperTwistCoachDashboardWidgetInternal::IsQueueRecoveryMemorySource(
CachedCoachPanelState.PreferredGuidanceSourceLabel);
const bool bQueueRecoveryFollowUp =
CachedCoachPanelState.PreferredGuidanceLane == EHyperTwistTrainingCoachGuidanceLane::FollowUp;
const bool bQueueRecoveryRecommended =
@ -4560,13 +4622,14 @@ void UHyperTwistCoachDashboardWidget::UpdateDashboardPresentation()
: (bQueueRecoveryRecommended
? TEXT("recommended")
: TEXT("none"));
const FString QueueRecoverySourceLabel = CachedCoachPanelState.PreferredGuidanceSourceLabel.IsEmpty()
? TEXT("n/a")
: CachedCoachPanelState.PreferredGuidanceSourceLabel;
const FString QueueRecoverySourceLabel =
HyperTwistCoachDashboardWidgetInternal::DescribeCoachMemorySourceLabel(
CachedCoachPanelState.PreferredGuidanceSourceLabel);
const FString QueueRecoveryActionSourceLabel =
CachedCoachPanelState.PreferredQueueRecoveryActionSourceLabel.IsEmpty()
? TEXT("n/a")
: CachedCoachPanelState.PreferredQueueRecoveryActionSourceLabel;
: HyperTwistCoachDashboardWidgetInternal::DescribeCoachMemorySourceLabel(
CachedCoachPanelState.PreferredQueueRecoveryActionSourceLabel);
const FHyperTwistCoachDashboardQueueRecoveryWeighting QueueRecoveryWeighting = BuildQueueRecoveryWeighting();
const bool bCanLaunchQueueRecovery =
CachedCoachPanelState.bCanStartQueuedRun
@ -4875,7 +4938,7 @@ void UHyperTwistCoachDashboardWidget::UpdateDashboardPresentation()
const FString GuidanceRationaleDetailLine =
ActivePreviewBrief != nullptr
? FString::Printf(
TEXT("Brief: %s | Action: %s | Deck: %s | Method: %s | Focus cases: %d | Review: %s | Cadence: %s | Variety: %s | Selected focus: %s | Matching signals: %d | Case signal: %s | Top signals: %s"),
TEXT("Brief: %s | Action: %s | Deck: %s | Method: %s | Focus cases: %d | Review: %s | Cadence: %s | Variety: %s | Guidance source: %s | Selected focus: %s | Matching signals: %d | Case signal: %s | Top signals: %s"),
ActivePreviewBrief->Headline.IsEmpty() ? TEXT("n/a") : *ActivePreviewBrief->Headline,
ActivePreviewBrief->PrimaryActionLabel.IsEmpty() ? TEXT("n/a") : *ActivePreviewBrief->PrimaryActionLabel,
ActivePreviewBrief->FocusDeckId.IsEmpty() ? TEXT("n/a") : *ActivePreviewBrief->FocusDeckId,
@ -4884,6 +4947,8 @@ void UHyperTwistCoachDashboardWidget::UpdateDashboardPresentation()
ActivePreviewBrief->bNeedsCoachReview ? TEXT("yes") : TEXT("no"),
ActivePreviewBrief->bNeedsCadenceRecovery ? TEXT("yes") : TEXT("no"),
ActivePreviewBrief->bNeedsVarietyRotation ? TEXT("yes") : TEXT("no"),
*HyperTwistCoachDashboardWidgetInternal::DescribeCoachMemorySourceLabel(
CachedCoachPanelState.PreferredGuidanceSourceLabel),
bSelectedPreviewCaseIsExplicitFocus ? TEXT("yes") : TEXT("no"),
SelectedPreviewCaseSignalCount,
*SelectedPreviewCaseSignalLine,
@ -5901,9 +5966,8 @@ void UHyperTwistCoachDashboardWidget::UpdateDashboardPresentation()
? TEXT("yes")
: TEXT("no"),
*ClosureGuidanceLaneLabel,
CachedCoachPanelState.PreferredGuidanceSourceLabel.IsEmpty()
? TEXT("n/a")
: *CachedCoachPanelState.PreferredGuidanceSourceLabel,
*HyperTwistCoachDashboardWidgetInternal::DescribeCoachMemorySourceLabel(
CachedCoachPanelState.PreferredGuidanceSourceLabel),
ClosureRecoverySignal != nullptr ? TEXT(" || Signal detail: ") : TEXT(""),
ClosureRecoverySignal != nullptr
? *ClosureRecoverySignal->Detail
@ -5950,9 +6014,8 @@ void UHyperTwistCoachDashboardWidget::UpdateDashboardPresentation()
SelectedClosureRecoveryEntry->bBorderlineFollowUpSuppressedByRecoveryHistory
? TEXT("yes")
: TEXT("no"),
SelectedClosureRecoveryEntry->PreferredGuidanceSourceLabel.IsEmpty()
? TEXT("n/a")
: *SelectedClosureRecoveryEntry->PreferredGuidanceSourceLabel,
*HyperTwistCoachDashboardWidgetInternal::DescribeCoachMemorySourceLabel(
SelectedClosureRecoveryEntry->PreferredGuidanceSourceLabel),
SelectedClosureRecoveryEntry->bActiveSignalPromoted ? TEXT("yes") : TEXT("no"),
SelectedClosureRecoveryEntry->ActiveSignalHeadline.IsEmpty()
? TEXT("n/a")

View file

@ -18,6 +18,55 @@ namespace HyperTwistTrainingCoachLibraryInternal
return EHyperTwistTrainingCoachGuidanceLane::None;
}
bool IsDashboardHandoffGuidanceSource(const FString& SourceLabel)
{
return SourceLabel.StartsWith(TEXT("coach-memory-dashboard-"));
}
bool IsQueueRecoveryMemorySource(const FString& SourceLabel)
{
return SourceLabel.StartsWith(TEXT("coach-memory-queue-"))
|| IsDashboardHandoffGuidanceSource(SourceLabel);
}
FString DescribeCoachMemorySourceLabel(const FString& SourceLabel)
{
if (SourceLabel == TEXT("coach-memory-dashboard-follow-up"))
{
return TEXT("accepted follow-up handoff history");
}
if (SourceLabel == TEXT("coach-memory-dashboard-recommended"))
{
return TEXT("accepted recommended handoff history");
}
if (SourceLabel == TEXT("coach-memory-queue-follow-up"))
{
return TEXT("queue follow-up recovery memory");
}
if (SourceLabel == TEXT("coach-memory-queue-recommended"))
{
return TEXT("queue recommended recovery memory");
}
if (SourceLabel == TEXT("dashboard-handoff-queued"))
{
return TEXT("accepted queued dashboard handoff");
}
if (SourceLabel == TEXT("queue-history-promote-pressure"))
{
return TEXT("queue promote-pressure history");
}
if (SourceLabel == TEXT("queue-history-launch-pressure"))
{
return TEXT("queue launch-pressure history");
}
if (SourceLabel == TEXT("queue-history-balanced-pressure"))
{
return TEXT("balanced queue-recovery history");
}
return SourceLabel.IsEmpty() ? TEXT("none") : SourceLabel;
}
float ResolvePreferredGuidanceLanePressure(
const FHyperTwistTrainingCoachMemorySnapshot& CoachMemorySnapshot)
{
@ -531,13 +580,18 @@ TArray<FHyperTwistCoachSignal> UHyperTwistTrainingCoachLibrary::DeriveCoachSigna
const bool bBlockedFollowUp = CoachMemorySnapshot.bHasBlockedFollowUpScheduleFriction;
const bool bRepeatManualReschedule = CoachMemorySnapshot.bHasRepeatManualScheduleFriction;
const bool bQueueRecoverySource =
CoachMemorySnapshot.PreferredGuidanceSourceLabel.StartsWith(TEXT("coach-memory-queue-"));
HyperTwistTrainingCoachLibraryInternal::IsQueueRecoveryMemorySource(
CoachMemorySnapshot.PreferredGuidanceSourceLabel);
const bool bQueueRecoveryFollowUp =
bQueueRecoverySource
&& CoachMemorySnapshot.PreferredGuidanceLane == EHyperTwistTrainingCoachGuidanceLane::FollowUp;
const bool bQueueRecoveryRecommended =
bQueueRecoverySource
&& CoachMemorySnapshot.PreferredGuidanceLane == EHyperTwistTrainingCoachGuidanceLane::Recommended;
const bool bDashboardFollowUpRecovery =
CoachMemorySnapshot.PreferredGuidanceSourceLabel == TEXT("coach-memory-dashboard-follow-up");
const bool bDashboardRecommendedRecovery =
CoachMemorySnapshot.PreferredGuidanceSourceLabel == TEXT("coach-memory-dashboard-recommended");
const bool bPromoteQueueRecovery =
CoachMemorySnapshot.PreferredQueueRecoveryAction
== EHyperTwistTrainingQueueRecoveryAction::PromoteDeferred;
@ -550,33 +604,61 @@ TArray<FHyperTwistCoachSignal> UHyperTwistTrainingCoachLibrary::DeriveCoachSigna
+ (bBlockedFollowUp ? 6.0f : 0.0f)
+ (bRepeatManualReschedule ? 5.0f : 0.0f)
+ (CoachMemorySnapshot.bHasQueueExecutionCompletionGap ? 4.0f : 0.0f)
+ (bQueueRecoverySource ? 2.0f : 0.0f);
+ (bQueueRecoverySource ? 2.0f : 0.0f)
+ ((bDashboardFollowUpRecovery || bDashboardRecommendedRecovery) ? 2.0f : 0.0f);
const EHyperTwistCoachFocusLane FocusLane = bArchiveAware
? EHyperTwistCoachFocusLane::Stability
: EHyperTwistCoachFocusLane::Cadence;
const FString Headline = bQueueRecoveryFollowUp
? (bPromoteQueueRecovery
FString Headline;
if (bDashboardFollowUpRecovery)
{
Headline = bPromoteQueueRecovery
? TEXT("Accepted follow-up handoffs keep the deferred follow-up lane active")
: TEXT("Accepted follow-up handoffs favor recovering the follow-up lane next");
}
else if (bDashboardRecommendedRecovery)
{
Headline = bPromoteQueueRecovery
? TEXT("Accepted recommended handoffs keep the deferred recommended lane active")
: TEXT("Accepted recommended handoffs favor restoring the recommended lane next");
}
else if (bQueueRecoveryFollowUp)
{
Headline = bPromoteQueueRecovery
? TEXT("Promote the deferred follow-up lane back into active queue flow")
: TEXT("Recover the deferred follow-up lane before the queue drifts further"))
: (bQueueRecoveryRecommended
? (bPromoteQueueRecovery
? TEXT("Promote the deferred recommended lane back into the active queue")
: TEXT("Recover the deferred recommended lane and restore queue cadence"))
: (bBlockedFollowUp
? (bPromoteQueueRecovery
? TEXT("Promote deferred work to relieve follow-up scheduling friction")
: TEXT("Follow-up scheduling friction is stalling the queue"))
: (bArchiveAware
? TEXT("Archive-aware scheduling friction needs cleanup")
: (bLaunchQueueRecovery
? TEXT("Launch recovery work to restore coaching queue cadence")
: TEXT("Coaching queue scheduling needs intervention")))));
: TEXT("Recover the deferred follow-up lane before the queue drifts further");
}
else if (bQueueRecoveryRecommended)
{
Headline = bPromoteQueueRecovery
? TEXT("Promote the deferred recommended lane back into the active queue")
: TEXT("Recover the deferred recommended lane and restore queue cadence");
}
else if (bBlockedFollowUp)
{
Headline = bPromoteQueueRecovery
? TEXT("Promote deferred work to relieve follow-up scheduling friction")
: TEXT("Follow-up scheduling friction is stalling the queue");
}
else if (bArchiveAware)
{
Headline = TEXT("Archive-aware scheduling friction needs cleanup");
}
else if (bLaunchQueueRecovery)
{
Headline = TEXT("Launch recovery work to restore coaching queue cadence");
}
else
{
Headline = TEXT("Coaching queue scheduling needs intervention");
}
const FString DeferralReasonLabel = !CoachMemorySnapshot.DominantScheduleDeferralReasonLabel.IsEmpty()
? CoachMemorySnapshot.DominantScheduleDeferralReasonLabel
: TEXT("none");
const FString RecoverySourceLabel = bQueueRecoverySource
? CoachMemorySnapshot.PreferredGuidanceSourceLabel
? HyperTwistTrainingCoachLibraryInternal::DescribeCoachMemorySourceLabel(
CoachMemorySnapshot.PreferredGuidanceSourceLabel)
: TEXT("none");
const FString RecoveryActionLabel =
CoachMemorySnapshot.PreferredQueueRecoveryAction == EHyperTwistTrainingQueueRecoveryAction::PromoteDeferred
@ -588,7 +670,8 @@ TArray<FHyperTwistCoachSignal> UHyperTwistTrainingCoachLibrary::DeriveCoachSigna
const FString RecoveryActionSourceLabel =
CoachMemorySnapshot.PreferredQueueRecoveryActionSourceLabel.IsEmpty()
? TEXT("none")
: CoachMemorySnapshot.PreferredQueueRecoveryActionSourceLabel;
: HyperTwistTrainingCoachLibraryInternal::DescribeCoachMemorySourceLabel(
CoachMemorySnapshot.PreferredQueueRecoveryActionSourceLabel);
const FString SignalId = bQueueRecoveryFollowUp
? (bPromoteQueueRecovery
? TEXT("schedule_friction_follow_up_promote_recovery")
@ -1556,7 +1639,8 @@ FHyperTwistCoachSignal UHyperTwistTrainingCoachLibrary::DeriveClosureRecoveryHis
CoachMemorySnapshot.FollowUpReadyCoachSequenceCount,
CoachMemorySnapshot.PreferredGuidanceSourceLabel.IsEmpty()
? TEXT("n/a")
: *CoachMemorySnapshot.PreferredGuidanceSourceLabel
: *HyperTwistTrainingCoachLibraryInternal::DescribeCoachMemorySourceLabel(
CoachMemorySnapshot.PreferredGuidanceSourceLabel)
);
const EHyperTwistTrainingDeliveryMode SuggestedMode = bHistoryPrefersFollowUp
? EHyperTwistTrainingDeliveryMode::CoachReviewed