diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistCoachDashboardWidget.cpp b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistCoachDashboardWidget.cpp index e7c2c50..d51b181 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistCoachDashboardWidget.cpp +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistCoachDashboardWidget.cpp @@ -9895,6 +9895,496 @@ UHyperTwistCoachDashboardWidget::GetDisplayedGuidancePreferenceInspectSurface() return Surface; } +FHyperTwistTrainingCoachDashboardGuidanceDeckPreviewInspectSurface +UHyperTwistCoachDashboardWidget::GetDisplayedRecommendedGuidanceDeckPreviewInspectSurface() const +{ + FHyperTwistTrainingCoachDashboardGuidanceDeckPreviewInspectSurface Surface; + Surface.Headline = TEXT("Recommended guidance"); + Surface.GuidanceLaneLabel = TEXT("recommended"); + + const auto BuildDeckPreviewLine = [](const FHyperTwistTrainingDeck& Deck, const FHyperTwistCoachBrief& Brief, const TCHAR* EmptyPrefix) + { + if (Deck.IsStructurallyValid()) + { + TArray PreviewCaseParts; + const int32 PreviewCaseCount = FMath::Min(Deck.Cases.Num(), 3); + for (int32 CaseIndex = 0; CaseIndex < PreviewCaseCount; ++CaseIndex) + { + const FHyperTwistTrainingCase& PreviewCase = Deck.Cases[CaseIndex]; + PreviewCaseParts.Add( + PreviewCase.PromptLabel.IsEmpty() ? PreviewCase.CaseId : PreviewCase.PromptLabel + ); + } + const FString PreviewCases = PreviewCaseParts.Num() > 0 + ? FString::Join(PreviewCaseParts, TEXT(" | ")) + : TEXT("none"); + return FString::Printf( + TEXT("Deck preview: %s | Deck: %s | Cases: %d | Selection: %s | Difficulty: %s | Subset: %s | Preview: %s"), + Deck.Title.IsEmpty() ? TEXT("n/a") : *Deck.Title, + Deck.DeckId.IsEmpty() ? TEXT("n/a") : *Deck.DeckId, + Deck.Cases.Num(), + *HyperTwistCoachDashboardWidgetInternal::EnumDisplayName(Deck.SelectionPolicy), + Deck.DifficultyBand.IsEmpty() ? TEXT("n/a") : *Deck.DifficultyBand, + Deck.SubsetId.IsEmpty() ? TEXT("n/a") : *Deck.SubsetId, + *PreviewCases); + } + + if (!Brief.UserId.IsEmpty() && !Brief.Headline.IsEmpty()) + { + return FString::Printf( + TEXT("%s: %s | Deck: %s | Method: %s | Focus cases: %d | Action: %s"), + EmptyPrefix, + *Brief.Headline, + Brief.FocusDeckId.IsEmpty() ? TEXT("n/a") : *Brief.FocusDeckId, + Brief.FocusMethodSegmentId.IsEmpty() ? TEXT("n/a") : *Brief.FocusMethodSegmentId, + Brief.FocusCaseIds.Num(), + Brief.PrimaryActionLabel.IsEmpty() ? TEXT("n/a") : *Brief.PrimaryActionLabel); + } + + return FString::Printf(TEXT("%s: no guidance available"), EmptyPrefix); + }; + + const bool bGuidanceReadyToLaunch = CachedCoachPanelState.bCanStartCoachRecommendedRun; + const FHyperTwistTrainingDeck DisplayedDeck = + bGuidanceReadyToLaunch ? CachedCoachRecommendedDeck : FHyperTwistTrainingDeck(); + const FHyperTwistCoachBrief DisplayedBrief = + bGuidanceReadyToLaunch ? CachedCoachBrief : FHyperTwistCoachBrief(); + + Surface.bGuidanceReadyToLaunch = bGuidanceReadyToLaunch; + Surface.bHasDeck = DisplayedDeck.IsStructurallyValid(); + Surface.bHasBrief = !DisplayedBrief.UserId.IsEmpty() && !DisplayedBrief.Headline.IsEmpty(); + Surface.bUsesBriefFallback = !Surface.bHasDeck && Surface.bHasBrief; + Surface.bActivePreviewLane = + ActiveGuidancePreviewLane == EHyperTwistCoachDashboardGuidancePreviewLane::Recommended; + Surface.DeckId = DisplayedDeck.DeckId.IsEmpty() ? TEXT("n/a") : DisplayedDeck.DeckId; + Surface.DeckTitle = DisplayedDeck.Title.IsEmpty() ? TEXT("n/a") : DisplayedDeck.Title; + Surface.DeckLabel = + DisplayedDeck.IsStructurallyValid() + ? (DisplayedDeck.Title.IsEmpty() ? Surface.DeckId : DisplayedDeck.Title) + : TEXT("n/a"); + Surface.BriefHeadline = + DisplayedBrief.Headline.IsEmpty() ? TEXT("n/a") : DisplayedBrief.Headline; + Surface.PrimaryActionLabel = + DisplayedBrief.PrimaryActionLabel.IsEmpty() ? TEXT("n/a") : DisplayedBrief.PrimaryActionLabel; + Surface.FocusDeckId = + DisplayedBrief.FocusDeckId.IsEmpty() ? TEXT("n/a") : DisplayedBrief.FocusDeckId; + Surface.FocusMethodSegmentId = + DisplayedBrief.FocusMethodSegmentId.IsEmpty() + ? TEXT("n/a") + : DisplayedBrief.FocusMethodSegmentId; + Surface.SelectionPolicyLabel = + DisplayedDeck.IsStructurallyValid() + ? HyperTwistCoachDashboardWidgetInternal::EnumDisplayName( + DisplayedDeck.SelectionPolicy) + : TEXT("n/a"); + Surface.SuggestedModeLabel = + Surface.bHasBrief + ? HyperTwistCoachDashboardWidgetInternal::EnumDisplayName(DisplayedBrief.SuggestedMode) + : TEXT("n/a"); + Surface.SuggestedSelectionLabel = + Surface.bHasBrief + ? HyperTwistCoachDashboardWidgetInternal::EnumDisplayName( + DisplayedBrief.SuggestedSelectionPolicy) + : TEXT("n/a"); + Surface.DifficultyBand = + DisplayedDeck.DifficultyBand.IsEmpty() ? TEXT("n/a") : DisplayedDeck.DifficultyBand; + Surface.SubsetId = DisplayedDeck.SubsetId.IsEmpty() ? TEXT("n/a") : DisplayedDeck.SubsetId; + Surface.CaseCount = DisplayedDeck.Cases.Num(); + Surface.FocusCaseCount = DisplayedBrief.FocusCaseIds.Num(); + + TArray PreviewCaseParts; + const int32 PreviewCaseCount = FMath::Min(DisplayedDeck.Cases.Num(), 3); + for (int32 CaseIndex = 0; CaseIndex < PreviewCaseCount; ++CaseIndex) + { + const FHyperTwistTrainingCase& PreviewCase = DisplayedDeck.Cases[CaseIndex]; + PreviewCaseParts.Add(PreviewCase.PromptLabel.IsEmpty() ? PreviewCase.CaseId : PreviewCase.PromptLabel); + } + Surface.PreviewCaseSummaryLine = + PreviewCaseParts.Num() > 0 ? FString::Join(PreviewCaseParts, TEXT(" | ")) : TEXT("none"); + + const bool bQueueRecoveryMemorySource = + HyperTwistCoachDashboardWidgetInternal::IsQueueRecoveryMemorySource( + CachedCoachPanelState.PreferredGuidanceSourceLabel); + Surface.bClosedLoopIdleDashboard = + IsCoachLoopActionabilityClosed() + && ( + bQueueRecoveryMemorySource + || !CachedCoachPanelState.PreferredGuidanceSourceLabel.IsEmpty() + || DisplayedCoachHandoffRecommendationKind != EHyperTwistTrainingCoachHandoffKind::None); + + Surface.StatusLine = !DisplayedRecommendedDeckPreviewLine.IsEmpty() + ? DisplayedRecommendedDeckPreviewLine + : BuildDeckPreviewLine(DisplayedDeck, DisplayedBrief, TEXT("Recommended guidance")); + Surface.DetailLine = Surface.bHasDeck + ? FString::Printf( + TEXT("Recommended preview detail: deck %s | selection %s | mode %s | difficulty %s | subset %s | focus deck %s | method %s | focus cases %d | preview %s"), + *Surface.DeckLabel, + *Surface.SelectionPolicyLabel, + *Surface.SuggestedModeLabel, + *Surface.DifficultyBand, + *Surface.SubsetId, + *Surface.FocusDeckId, + *Surface.FocusMethodSegmentId, + Surface.FocusCaseCount, + *Surface.PreviewCaseSummaryLine) + : (Surface.bHasBrief + ? FString::Printf( + TEXT("Recommended preview detail: %s | action %s | focus deck %s | method %s | focus cases %d"), + *Surface.BriefHeadline, + *Surface.PrimaryActionLabel, + *Surface.FocusDeckId, + *Surface.FocusMethodSegmentId, + Surface.FocusCaseCount) + : TEXT("Recommended preview detail: no recommended guidance is currently available.")); + if (Surface.bClosedLoopIdleDashboard) + { + Surface.StatusLine = + TEXT("Recommended guidance: no active recommended guidance; the coach-to-review loop is closed and idle."); + Surface.DetailLine = + TEXT("Recommended preview detail: retained coach memory remains inspectable, but no live recommended preview deck remains active."); + } + return Surface; +} + +FHyperTwistTrainingCoachDashboardGuidanceDeckPreviewInspectSurface +UHyperTwistCoachDashboardWidget::GetDisplayedFollowUpGuidanceDeckPreviewInspectSurface() const +{ + FHyperTwistTrainingCoachDashboardGuidanceDeckPreviewInspectSurface Surface; + Surface.Headline = TEXT("Follow-up guidance"); + Surface.GuidanceLaneLabel = TEXT("follow-up"); + + const auto BuildDeckPreviewLine = [](const FHyperTwistTrainingDeck& Deck, const FHyperTwistCoachBrief& Brief, const TCHAR* EmptyPrefix) + { + if (Deck.IsStructurallyValid()) + { + TArray PreviewCaseParts; + const int32 PreviewCaseCount = FMath::Min(Deck.Cases.Num(), 3); + for (int32 CaseIndex = 0; CaseIndex < PreviewCaseCount; ++CaseIndex) + { + const FHyperTwistTrainingCase& PreviewCase = Deck.Cases[CaseIndex]; + PreviewCaseParts.Add( + PreviewCase.PromptLabel.IsEmpty() ? PreviewCase.CaseId : PreviewCase.PromptLabel + ); + } + const FString PreviewCases = PreviewCaseParts.Num() > 0 + ? FString::Join(PreviewCaseParts, TEXT(" | ")) + : TEXT("none"); + return FString::Printf( + TEXT("Deck preview: %s | Deck: %s | Cases: %d | Selection: %s | Difficulty: %s | Subset: %s | Preview: %s"), + Deck.Title.IsEmpty() ? TEXT("n/a") : *Deck.Title, + Deck.DeckId.IsEmpty() ? TEXT("n/a") : *Deck.DeckId, + Deck.Cases.Num(), + *HyperTwistCoachDashboardWidgetInternal::EnumDisplayName(Deck.SelectionPolicy), + Deck.DifficultyBand.IsEmpty() ? TEXT("n/a") : *Deck.DifficultyBand, + Deck.SubsetId.IsEmpty() ? TEXT("n/a") : *Deck.SubsetId, + *PreviewCases); + } + + if (!Brief.UserId.IsEmpty() && !Brief.Headline.IsEmpty()) + { + return FString::Printf( + TEXT("%s: %s | Deck: %s | Method: %s | Focus cases: %d | Action: %s"), + EmptyPrefix, + *Brief.Headline, + Brief.FocusDeckId.IsEmpty() ? TEXT("n/a") : *Brief.FocusDeckId, + Brief.FocusMethodSegmentId.IsEmpty() ? TEXT("n/a") : *Brief.FocusMethodSegmentId, + Brief.FocusCaseIds.Num(), + Brief.PrimaryActionLabel.IsEmpty() ? TEXT("n/a") : *Brief.PrimaryActionLabel); + } + + return FString::Printf(TEXT("%s: no guidance available"), EmptyPrefix); + }; + + const bool bGuidanceReadyToLaunch = + CachedCoachPanelState.bCanStartCoachFollowUpRun + && CachedCoachPanelState.bHasFollowUpGuidance; + const FHyperTwistTrainingDeck DisplayedDeck = + bGuidanceReadyToLaunch ? CachedCoachFollowUpDeck : FHyperTwistTrainingDeck(); + const FHyperTwistCoachBrief DisplayedBrief = + bGuidanceReadyToLaunch ? CachedCoachFollowUpBrief : FHyperTwistCoachBrief(); + + Surface.bGuidanceReadyToLaunch = bGuidanceReadyToLaunch; + Surface.bHasDeck = DisplayedDeck.IsStructurallyValid(); + Surface.bHasBrief = !DisplayedBrief.UserId.IsEmpty() && !DisplayedBrief.Headline.IsEmpty(); + Surface.bUsesBriefFallback = !Surface.bHasDeck && Surface.bHasBrief; + Surface.bActivePreviewLane = + ActiveGuidancePreviewLane == EHyperTwistCoachDashboardGuidancePreviewLane::FollowUp; + Surface.DeckId = DisplayedDeck.DeckId.IsEmpty() ? TEXT("n/a") : DisplayedDeck.DeckId; + Surface.DeckTitle = DisplayedDeck.Title.IsEmpty() ? TEXT("n/a") : DisplayedDeck.Title; + Surface.DeckLabel = + DisplayedDeck.IsStructurallyValid() + ? (DisplayedDeck.Title.IsEmpty() ? Surface.DeckId : DisplayedDeck.Title) + : TEXT("n/a"); + Surface.BriefHeadline = + DisplayedBrief.Headline.IsEmpty() ? TEXT("n/a") : DisplayedBrief.Headline; + Surface.PrimaryActionLabel = + DisplayedBrief.PrimaryActionLabel.IsEmpty() ? TEXT("n/a") : DisplayedBrief.PrimaryActionLabel; + Surface.FocusDeckId = + DisplayedBrief.FocusDeckId.IsEmpty() ? TEXT("n/a") : DisplayedBrief.FocusDeckId; + Surface.FocusMethodSegmentId = + DisplayedBrief.FocusMethodSegmentId.IsEmpty() + ? TEXT("n/a") + : DisplayedBrief.FocusMethodSegmentId; + Surface.SelectionPolicyLabel = + DisplayedDeck.IsStructurallyValid() + ? HyperTwistCoachDashboardWidgetInternal::EnumDisplayName( + DisplayedDeck.SelectionPolicy) + : TEXT("n/a"); + Surface.SuggestedModeLabel = + Surface.bHasBrief + ? HyperTwistCoachDashboardWidgetInternal::EnumDisplayName(DisplayedBrief.SuggestedMode) + : TEXT("n/a"); + Surface.SuggestedSelectionLabel = + Surface.bHasBrief + ? HyperTwistCoachDashboardWidgetInternal::EnumDisplayName( + DisplayedBrief.SuggestedSelectionPolicy) + : TEXT("n/a"); + Surface.DifficultyBand = + DisplayedDeck.DifficultyBand.IsEmpty() ? TEXT("n/a") : DisplayedDeck.DifficultyBand; + Surface.SubsetId = DisplayedDeck.SubsetId.IsEmpty() ? TEXT("n/a") : DisplayedDeck.SubsetId; + Surface.CaseCount = DisplayedDeck.Cases.Num(); + Surface.FocusCaseCount = DisplayedBrief.FocusCaseIds.Num(); + + TArray PreviewCaseParts; + const int32 PreviewCaseCount = FMath::Min(DisplayedDeck.Cases.Num(), 3); + for (int32 CaseIndex = 0; CaseIndex < PreviewCaseCount; ++CaseIndex) + { + const FHyperTwistTrainingCase& PreviewCase = DisplayedDeck.Cases[CaseIndex]; + PreviewCaseParts.Add(PreviewCase.PromptLabel.IsEmpty() ? PreviewCase.CaseId : PreviewCase.PromptLabel); + } + Surface.PreviewCaseSummaryLine = + PreviewCaseParts.Num() > 0 ? FString::Join(PreviewCaseParts, TEXT(" | ")) : TEXT("none"); + + const bool bQueueRecoveryMemorySource = + HyperTwistCoachDashboardWidgetInternal::IsQueueRecoveryMemorySource( + CachedCoachPanelState.PreferredGuidanceSourceLabel); + Surface.bClosedLoopIdleDashboard = + IsCoachLoopActionabilityClosed() + && ( + bQueueRecoveryMemorySource + || !CachedCoachPanelState.PreferredGuidanceSourceLabel.IsEmpty() + || DisplayedCoachHandoffRecommendationKind != EHyperTwistTrainingCoachHandoffKind::None); + + Surface.StatusLine = !DisplayedFollowUpDeckPreviewLine.IsEmpty() + ? DisplayedFollowUpDeckPreviewLine + : BuildDeckPreviewLine(DisplayedDeck, DisplayedBrief, TEXT("Follow-up guidance")); + Surface.DetailLine = Surface.bHasDeck + ? FString::Printf( + TEXT("Follow-up preview detail: deck %s | selection %s | mode %s | difficulty %s | subset %s | focus deck %s | method %s | focus cases %d | preview %s"), + *Surface.DeckLabel, + *Surface.SelectionPolicyLabel, + *Surface.SuggestedModeLabel, + *Surface.DifficultyBand, + *Surface.SubsetId, + *Surface.FocusDeckId, + *Surface.FocusMethodSegmentId, + Surface.FocusCaseCount, + *Surface.PreviewCaseSummaryLine) + : (Surface.bHasBrief + ? FString::Printf( + TEXT("Follow-up preview detail: %s | action %s | focus deck %s | method %s | focus cases %d"), + *Surface.BriefHeadline, + *Surface.PrimaryActionLabel, + *Surface.FocusDeckId, + *Surface.FocusMethodSegmentId, + Surface.FocusCaseCount) + : TEXT("Follow-up preview detail: no follow-up guidance is currently available.")); + if (Surface.bClosedLoopIdleDashboard) + { + Surface.StatusLine = + TEXT("Follow-up guidance: no active follow-up guidance; the coach-to-review loop is closed and idle."); + Surface.DetailLine = + TEXT("Follow-up preview detail: retained coach memory remains inspectable, but no live follow-up preview deck remains active."); + } + return Surface; +} + +FHyperTwistTrainingCoachDashboardHandoffRecommendationInspectSurface +UHyperTwistCoachDashboardWidget::GetDisplayedCoachHandoffRecommendationInspectSurface() const +{ + FHyperTwistTrainingCoachDashboardHandoffRecommendationInspectSurface Surface; + Surface.Headline = TEXT("Handoff recommendation"); + + const HyperTwistCoachDashboardWidgetInternal::FHandoffHistoryDashboardBias HandoffHistoryBias = + HyperTwistCoachDashboardWidgetInternal::DeriveHandoffHistoryDashboardBias( + HandoffHistoryEntries); + const HyperTwistCoachDashboardWidgetInternal::FMethodDrillRecoveryPacketDashboardBias + MethodDrillRecoveryPacketHandoffBias = + HyperTwistCoachDashboardWidgetInternal::DeriveMethodDrillRecoveryPacketDashboardBias( + MethodDrillRecoveryMemoryHistoryEntries); + const HyperTwistCoachDashboardWidgetInternal::FMethodDrillRecoveryPlanComparisonSummary + MethodDrillRecoveryPlanComparisonSummary = + HyperTwistCoachDashboardWidgetInternal::DeriveMethodDrillRecoveryPlanComparisonSummary( + MethodDrillRecoveryMemoryHistoryEntries); + const HyperTwistCoachDashboardWidgetInternal::EMethodDrillRecoveryMemoryBias + RetainedMethodDrillRecoveryMemoryHandoffBias = + HyperTwistCoachDashboardWidgetInternal::DeriveRetainedMethodDrillRecoveryMemoryBias( + MethodDrillRecoveryMemoryHistoryEntries, + false); + const HyperTwistCoachDashboardWidgetInternal::EMethodDrillRecoveryMemoryBias + CurrentMethodDrillRecoveryMemoryBias = + HyperTwistCoachDashboardWidgetInternal::ResolveMethodDrillRecoveryMemoryBias( + CachedCoachPanelState.MethodDrillRecoveryHistoryCount, + CachedCoachPanelState.StrongMethodDrillRecoveryHistoryCount, + CachedCoachPanelState.WeakMethodDrillRecoveryHistoryCount, + CachedCoachPanelState.MethodDrillRecoveryAverageOutcomeScore); + const HyperTwistCoachDashboardWidgetInternal::EMethodDrillRecoveryMemoryBias + EffectiveMethodDrillRecoveryMemoryHandoffBias = + RetainedMethodDrillRecoveryMemoryHandoffBias + != HyperTwistCoachDashboardWidgetInternal::EMethodDrillRecoveryMemoryBias::None + ? RetainedMethodDrillRecoveryMemoryHandoffBias + : CurrentMethodDrillRecoveryMemoryBias; + const bool bHasActiveRun = CachedRunState.Session.IsStructurallyValid() + && CachedRunState.ActiveDeck.IsStructurallyValid(); + const auto DescribeDisplayedHandoffKind = [](const EHyperTwistTrainingCoachHandoffKind Kind) -> FString + { + switch (Kind) + { + case EHyperTwistTrainingCoachHandoffKind::Queue: + return TEXT("queue"); + case EHyperTwistTrainingCoachHandoffKind::FollowUp: + return TEXT("follow-up"); + case EHyperTwistTrainingCoachHandoffKind::Recommended: + return TEXT("recommended"); + default: + return TEXT("none"); + } + }; + + Surface.bQueueReady = CachedCoachPanelState.bCanStartQueuedRun; + Surface.bFollowUpReady = + CachedCoachPanelState.bCanStartCoachFollowUpRun + && CachedCoachPanelState.bHasFollowUpGuidance; + Surface.bRecommendedReady = CachedCoachPanelState.bCanStartCoachRecommendedRun; + Surface.bCanUseCoachHandoff = !bHasLiveAttemptTimer && !bHasActiveRun; + Surface.bHasDeferredFirstSuppressionSignal = + HyperTwistCoachDashboardWidgetInternal::FindCoachSignalById( + CachedCoachSignals, + TEXT("queue_suppression_deferred_first_continuation")) + != nullptr; + Surface.RecommendationKindLabel = + DescribeDisplayedHandoffKind(DisplayedCoachHandoffRecommendationKind); + Surface.PreferredHandoffHistoryKindLabel = + HyperTwistCoachDashboardWidgetInternal::DescribeHandoffRecommendationKind( + HandoffHistoryBias.PreferredKind); + Surface.QueueSourceLabel = + CachedCoachPanelState.NextQueueSourceLabel.IsEmpty() + ? TEXT("n/a") + : CachedCoachPanelState.NextQueueSourceLabel; + Surface.PreferredGuidanceLaneLabel = + HyperTwistCoachDashboardWidgetInternal::DescribeGuidanceLane( + CachedCoachPanelState.PreferredGuidanceLane); + Surface.PreferredGuidanceSourceLabel = + CachedCoachPanelState.PreferredGuidanceSourceLabel.IsEmpty() + ? TEXT("n/a") + : CachedCoachPanelState.PreferredGuidanceSourceLabel; + Surface.PreferredGuidanceSourceDescription = + HyperTwistCoachDashboardWidgetInternal::DescribeCoachMemorySourceLabel( + CachedCoachPanelState.PreferredGuidanceSourceLabel); + Surface.LaunchBiasLabel = + HyperTwistCoachDashboardWidgetInternal::DescribeLaunchBudgetBias( + CachedCoachPanelState.PreferredLaunchBudgetBias); + Surface.LaunchBiasSourceLabel = + HyperTwistCoachDashboardWidgetInternal::DescribeCoachMemorySourceLabel( + CachedCoachPanelState.PreferredLaunchBudgetSourceLabel); + Surface.MethodDrillRecoveryPostureLabel = + HyperTwistCoachDashboardWidgetInternal::DescribeMethodDrillRecoveryMemoryBias( + EffectiveMethodDrillRecoveryMemoryHandoffBias); + Surface.MethodDrillRecoveryPacketPreferredKindLabel = + HyperTwistCoachDashboardWidgetInternal::DescribeHandoffRecommendationKind( + MethodDrillRecoveryPacketHandoffBias.PreferredHandoffKind); + Surface.MethodDrillRecoveryPlanPreferenceLabel = + HyperTwistCoachDashboardWidgetInternal::DescribeMethodDrillRecoveryPlanComparisonPreference( + MethodDrillRecoveryPlanComparisonSummary); + Surface.StartActionSourceLabel = + DisplayedCoachHandoffRecommendationKind != EHyperTwistTrainingCoachHandoffKind::None + ? ResolveDashboardHandoffStartActionSourceLabel( + DisplayedCoachHandoffRecommendationKind) + : TEXT("n/a"); + Surface.AcceptedQueuedHandoffCount = HandoffHistoryBias.QueueAcceptedDeltaCount; + Surface.AcceptedFollowUpHandoffCount = HandoffHistoryBias.FollowUpAcceptedDeltaCount; + Surface.AcceptedRecommendedHandoffCount = HandoffHistoryBias.RecommendedAcceptedDeltaCount; + Surface.QueueRecommendationCount = HandoffHistoryBias.QueueRecommendationCount; + Surface.FollowUpRecommendationCount = HandoffHistoryBias.FollowUpRecommendationCount; + Surface.RecommendedRecommendationCount = HandoffHistoryBias.RecommendedRecommendationCount; + Surface.StrongMethodDrillRecoveryHistoryCount = + CachedCoachPanelState.StrongMethodDrillRecoveryHistoryCount; + Surface.WeakMethodDrillRecoveryHistoryCount = + CachedCoachPanelState.WeakMethodDrillRecoveryHistoryCount; + Surface.MethodDrillRecoveryHistoryCount = + CachedCoachPanelState.MethodDrillRecoveryHistoryCount; + Surface.PacketQueueHistoryCount = MethodDrillRecoveryPacketHandoffBias.QueueEntryHistoryCount; + Surface.PacketFollowUpHistoryCount = + MethodDrillRecoveryPacketHandoffBias.FollowUpPacketHistoryCount; + Surface.PacketRecommendedHistoryCount = + MethodDrillRecoveryPacketHandoffBias.RecommendedBriefHistoryCount; + Surface.bHasRecommendation = + DisplayedCoachHandoffRecommendationKind != EHyperTwistTrainingCoachHandoffKind::None; + Surface.bUsedDeferredFirstSuppressionSignal = + bDisplayedCoachHandoffRecommendationUsedDeferredFirstSuppressionSignal; + Surface.bUsedMethodDrillWeighting = + bDisplayedCoachHandoffRecommendationUsedMethodDrillWeighting; + Surface.bUsedAlignedTightPacketAcceptedHistory = + bDisplayedCoachHandoffRecommendationUsedAlignedTightPacketAcceptedHistory; + Surface.bUsedAlignedBroadPacketAcceptedHistory = + bDisplayedCoachHandoffRecommendationUsedAlignedBroadPacketAcceptedHistory; + Surface.bUsedAcceptedAlignedTightPacketOutcomeTrust = + bDisplayedCoachHandoffRecommendationUsedAcceptedAlignedTightPacketOutcomeTrust; + Surface.bUsedAcceptedAlignedBroadPacketOutcomeTrust = + bDisplayedCoachHandoffRecommendationUsedAcceptedAlignedBroadPacketOutcomeTrust; + Surface.bUsedReliableAcceptedAlignedTightPacketOutcomeTrust = + bDisplayedCoachHandoffRecommendationUsedReliableAcceptedAlignedTightPacketOutcomeTrust; + Surface.bUsedReliableAcceptedAlignedBroadPacketOutcomeTrust = + bDisplayedCoachHandoffRecommendationUsedReliableAcceptedAlignedBroadPacketOutcomeTrust; + Surface.bUsedReliableAcceptedAlignedTightPacketOutcomeHistory = + bDisplayedCoachHandoffRecommendationUsedReliableAcceptedAlignedTightPacketOutcomeHistory; + Surface.bUsedReliableAcceptedAlignedBroadPacketOutcomeHistory = + bDisplayedCoachHandoffRecommendationUsedReliableAcceptedAlignedBroadPacketOutcomeHistory; + Surface.bUsedReliableAcceptedAlignedTightPacketExactHistory = + bDisplayedCoachHandoffRecommendationUsedReliableAcceptedAlignedTightPacketExactHistory; + Surface.bUsedReliableAcceptedAlignedBroadPacketExactHistory = + bDisplayedCoachHandoffRecommendationUsedReliableAcceptedAlignedBroadPacketExactHistory; + Surface.bUsedReliableAcceptedAlignedTightPacketExactOutcomeHistory = + bDisplayedCoachHandoffRecommendationUsedReliableAcceptedAlignedTightPacketExactOutcomeHistory; + Surface.bUsedReliableAcceptedAlignedBroadPacketExactOutcomeHistory = + bDisplayedCoachHandoffRecommendationUsedReliableAcceptedAlignedBroadPacketExactOutcomeHistory; + Surface.bUsedReliableAcceptedAlignedTightPacketExactOutcomeAcceptedHistory = + bDisplayedCoachHandoffRecommendationUsedReliableAcceptedAlignedTightPacketExactOutcomeAcceptedHistory; + Surface.bUsedReliableAcceptedAlignedBroadPacketExactOutcomeAcceptedHistory = + bDisplayedCoachHandoffRecommendationUsedReliableAcceptedAlignedBroadPacketExactOutcomeAcceptedHistory; + Surface.bUsedReliableAcceptedAlignedTightPacketExactOutcomeReliableHistory = + bDisplayedCoachHandoffRecommendationUsedReliableAcceptedAlignedTightPacketExactOutcomeReliableHistory; + Surface.bUsedReliableAcceptedAlignedBroadPacketExactOutcomeReliableHistory = + bDisplayedCoachHandoffRecommendationUsedReliableAcceptedAlignedBroadPacketExactOutcomeReliableHistory; + + const bool bQueueRecoveryMemorySource = + HyperTwistCoachDashboardWidgetInternal::IsQueueRecoveryMemorySource( + CachedCoachPanelState.PreferredGuidanceSourceLabel); + Surface.bClosedLoopIdleDashboard = + IsCoachLoopActionabilityClosed() + && ( + bQueueRecoveryMemorySource + || !CachedCoachPanelState.PreferredGuidanceSourceLabel.IsEmpty() + || DisplayedCoachHandoffRecommendationKind != EHyperTwistTrainingCoachHandoffKind::None); + + Surface.StatusLine = !DisplayedCoachHandoffRecommendationStatusLine.IsEmpty() + ? DisplayedCoachHandoffRecommendationStatusLine + : TEXT("Recommendation: no coach-directed handoff is currently ready."); + Surface.DetailLine = !DisplayedCoachHandoffRecommendationDetailLine.IsEmpty() + ? DisplayedCoachHandoffRecommendationDetailLine + : TEXT("Handoff recommendation detail: closure-history pressure is retained, but no displayed handoff recommendation is currently active."); + if (Surface.bClosedLoopIdleDashboard) + { + Surface.StatusLine = !DisplayedCoachHandoffRecommendationStatusLine.IsEmpty() + ? DisplayedCoachHandoffRecommendationStatusLine + : TEXT("Handoff recommendation: none; the coach-to-review loop is idle."); + Surface.DetailLine = !DisplayedCoachHandoffRecommendationDetailLine.IsEmpty() + ? DisplayedCoachHandoffRecommendationDetailLine + : TEXT("Handoff detail: retained closure history remains inspectable, but no queued, follow-up, or recommended continuation is active."); + } + return Surface; +} + bool UHyperTwistCoachDashboardWidget::SelectPreviousMethodDrillFollowUpHistoryEntry() { if (MethodDrillFollowUpHistoryEntries.Num() <= 0) @@ -20239,6 +20729,10 @@ void UHyperTwistCoachDashboardWidget::UpdateDashboardPresentation() CoachHandoffRecommendationDetailLine = TEXT("Handoff detail: retained closure history remains inspectable, but no queued, follow-up, or recommended continuation is active."); } + DisplayedRecommendedDeckPreviewLine = RecommendedDeckPreviewLine; + DisplayedFollowUpDeckPreviewLine = FollowUpDeckPreviewLine; + DisplayedCoachHandoffRecommendationStatusLine = CoachHandoffRecommendationStatusLine; + DisplayedCoachHandoffRecommendationDetailLine = CoachHandoffRecommendationDetailLine; RecordCoachHandoffHistorySnapshot( HyperTwistCoachDashboardWidgetInternal::DescribeHandoffRecommendationKind( HandoffRecommendationKind), diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistCoachDashboardWidget.h b/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistCoachDashboardWidget.h index d526e58..db7d7f5 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistCoachDashboardWidget.h +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistCoachDashboardWidget.h @@ -791,6 +791,18 @@ public: FHyperTwistTrainingCoachDashboardGuidancePreferenceInspectSurface GetDisplayedGuidancePreferenceInspectSurface() const; + UFUNCTION(BlueprintPure, Category = "HyperTwist|Training|Coach|Dashboard") + FHyperTwistTrainingCoachDashboardGuidanceDeckPreviewInspectSurface + GetDisplayedRecommendedGuidanceDeckPreviewInspectSurface() const; + + UFUNCTION(BlueprintPure, Category = "HyperTwist|Training|Coach|Dashboard") + FHyperTwistTrainingCoachDashboardGuidanceDeckPreviewInspectSurface + GetDisplayedFollowUpGuidanceDeckPreviewInspectSurface() const; + + UFUNCTION(BlueprintPure, Category = "HyperTwist|Training|Coach|Dashboard") + FHyperTwistTrainingCoachDashboardHandoffRecommendationInspectSurface + GetDisplayedCoachHandoffRecommendationInspectSurface() const; + UFUNCTION(BlueprintCallable, Category = "HyperTwist|Training|Coach|Dashboard") bool SelectPreviousMethodDrillFollowUpHistoryEntry(); @@ -1828,6 +1840,10 @@ private: bool bDisplayedCoachHandoffRecommendationUsedReliableAcceptedAlignedTightPacketExactOutcomeReliableHistory = false; bool bDisplayedCoachHandoffRecommendationUsedReliableAcceptedAlignedBroadPacketExactOutcomeReliableHistory = false; FString DisplayedCoachHandoffMethodDrillRecoveryMemoryStartActionSourceLabel; + FString DisplayedCoachHandoffRecommendationStatusLine; + FString DisplayedCoachHandoffRecommendationDetailLine; + FString DisplayedRecommendedDeckPreviewLine; + FString DisplayedFollowUpDeckPreviewLine; EHyperTwistCoachDashboardGuidancePreviewLane ActiveGuidancePreviewLane = EHyperTwistCoachDashboardGuidancePreviewLane::None; int32 SelectedGuidancePreviewCaseIndex = INDEX_NONE; diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingTypes.h b/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingTypes.h index ef6be93..e896663 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingTypes.h +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingTypes.h @@ -10559,6 +10559,268 @@ struct FHyperTwistTrainingCoachDashboardGuidancePreferenceInspectSurface } }; +USTRUCT(BlueprintType) +struct FHyperTwistTrainingCoachDashboardGuidanceDeckPreviewInspectSurface +{ + GENERATED_BODY() + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString Headline; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString StatusLine; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString DetailLine; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString GuidanceLaneLabel; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString DeckId; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString DeckTitle; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString DeckLabel; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString BriefHeadline; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString PrimaryActionLabel; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString FocusDeckId; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString FocusMethodSegmentId; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString SelectionPolicyLabel; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString SuggestedModeLabel; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString SuggestedSelectionLabel; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString DifficultyBand; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString SubsetId; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString PreviewCaseSummaryLine; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + int32 CaseCount = 0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + int32 FocusCaseCount = 0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bGuidanceReadyToLaunch = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bHasDeck = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bHasBrief = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bUsesBriefFallback = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bActivePreviewLane = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bClosedLoopIdleDashboard = false; + + bool IsStructurallyValid() const + { + return !StatusLine.IsEmpty() + || !DetailLine.IsEmpty() + || bHasDeck + || bHasBrief + || bGuidanceReadyToLaunch; + } +}; + +USTRUCT(BlueprintType) +struct FHyperTwistTrainingCoachDashboardHandoffRecommendationInspectSurface +{ + GENERATED_BODY() + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString Headline; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString StatusLine; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString DetailLine; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString RecommendationKindLabel; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString PreferredHandoffHistoryKindLabel; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString QueueSourceLabel; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString PreferredGuidanceLaneLabel; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString PreferredGuidanceSourceLabel; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString PreferredGuidanceSourceDescription; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString LaunchBiasLabel; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString LaunchBiasSourceLabel; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString MethodDrillRecoveryPostureLabel; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString MethodDrillRecoveryPacketPreferredKindLabel; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString MethodDrillRecoveryPlanPreferenceLabel; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString StartActionSourceLabel; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + int32 AcceptedQueuedHandoffCount = 0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + int32 AcceptedFollowUpHandoffCount = 0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + int32 AcceptedRecommendedHandoffCount = 0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + int32 QueueRecommendationCount = 0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + int32 FollowUpRecommendationCount = 0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + int32 RecommendedRecommendationCount = 0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + int32 StrongMethodDrillRecoveryHistoryCount = 0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + int32 WeakMethodDrillRecoveryHistoryCount = 0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + int32 MethodDrillRecoveryHistoryCount = 0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + int32 PacketQueueHistoryCount = 0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + int32 PacketFollowUpHistoryCount = 0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + int32 PacketRecommendedHistoryCount = 0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bHasRecommendation = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bCanUseCoachHandoff = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bQueueReady = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bFollowUpReady = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bRecommendedReady = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bHasDeferredFirstSuppressionSignal = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bClosedLoopIdleDashboard = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bUsedDeferredFirstSuppressionSignal = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bUsedMethodDrillWeighting = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bUsedAlignedTightPacketAcceptedHistory = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bUsedAlignedBroadPacketAcceptedHistory = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bUsedAcceptedAlignedTightPacketOutcomeTrust = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bUsedAcceptedAlignedBroadPacketOutcomeTrust = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bUsedReliableAcceptedAlignedTightPacketOutcomeTrust = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bUsedReliableAcceptedAlignedBroadPacketOutcomeTrust = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bUsedReliableAcceptedAlignedTightPacketOutcomeHistory = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bUsedReliableAcceptedAlignedBroadPacketOutcomeHistory = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bUsedReliableAcceptedAlignedTightPacketExactHistory = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bUsedReliableAcceptedAlignedBroadPacketExactHistory = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bUsedReliableAcceptedAlignedTightPacketExactOutcomeHistory = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bUsedReliableAcceptedAlignedBroadPacketExactOutcomeHistory = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bUsedReliableAcceptedAlignedTightPacketExactOutcomeAcceptedHistory = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bUsedReliableAcceptedAlignedBroadPacketExactOutcomeAcceptedHistory = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bUsedReliableAcceptedAlignedTightPacketExactOutcomeReliableHistory = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bUsedReliableAcceptedAlignedBroadPacketExactOutcomeReliableHistory = false; + + bool IsStructurallyValid() const + { + return !StatusLine.IsEmpty() + || !DetailLine.IsEmpty() + || bHasRecommendation + || bQueueReady + || bFollowUpReady + || bRecommendedReady; + } +}; + USTRUCT(BlueprintType) struct FHyperTwistTrainingCoachGeneratedModeLaunchExecutionContextSurface { diff --git a/docs/HYPERTWIST_ROADMAP_OVERHAUL_EXPANSION_GUIDE.md b/docs/HYPERTWIST_ROADMAP_OVERHAUL_EXPANSION_GUIDE.md index 1a313d7..37cdd0e 100644 --- a/docs/HYPERTWIST_ROADMAP_OVERHAUL_EXPANSION_GUIDE.md +++ b/docs/HYPERTWIST_ROADMAP_OVERHAUL_EXPANSION_GUIDE.md @@ -69,8 +69,8 @@ Current correction call from the source-exposed audit and current execution refr - the latest landed bounded `Phase 4` packet closes the retained-idle comparison/outcome actionability gap at the end of the recognition-assisted coach-to-review continuity lane, and the final retained-surface confirmation pass now leaves that closure lane functionally complete - the retained idle widget surface now keeps retained comparison and decision history inspectable without continuing to advertise live launch or guidance-outcome actions once the coach-to-review loop has already collapsed to truthful closed-loop idle - the now-complete deliberate `UHyperTwistTrainingRuntimeLibrary` world-context consumer lane reaches through active generated-mode selector option owned-execution split-phase current-selection merged ordered roster phase-id lookup, normalized-order lookup, and evaluation-order lookup inspect surfaces, so gameplay/Blueprint callers can inspect one retained merged-roster row directly by phase id, normalized order, or evaluation order without scanning the merged roster array or re-running inspect derivation details directly - - the latest landed bounded `Phase 4` packet stays on that same live `UHyperTwistCoachDashboardWidget` guidance-consumer lane and adds displayed guidance preview selection, displayed preview-case detail, displayed guidance rationale, and displayed guidance-preference inspect surfaces over already-derived preview brief/deck state and cached coach preference state, so gameplay/Blueprint callers can inspect the surfaced preview target, case detail, rationale signal posture, and lane-preference bias without scraping dashboard text blocks or reopening preview/preference derivation - - the next clean move is to stay on that same live dashboard guidance-consumer lane and land the displayed recommended guidance deck preview, displayed follow-up guidance deck preview, and live coach handoff recommendation inspect surface group rather than drifting back into backend continuity or reopening the already-complete retained-history seam + - the latest landed bounded `Phase 4` packet stays on that same live `UHyperTwistCoachDashboardWidget` guidance-consumer lane and adds displayed recommended guidance deck preview, displayed follow-up guidance deck preview, and displayed coach handoff recommendation inspect surfaces over already-cached deck/brief state and the live retained handoff recommendation posture, so gameplay/Blueprint callers can inspect the surfaced deck previews and current handoff recommendation without scraping dashboard text blocks or reopening preview/handoff derivation + - the next clean move is to stay on that same live dashboard guidance-consumer lane and land the displayed coach handoff status, displayed transition status, and displayed continuation / queued / follow-up / recommended run action-label inspect surface group rather than drifting back into backend continuity or reopening the already-complete retained-history seam - the current execution-discipline rule that broad refactor / monolith-splitting work should not interrupt the active bounded roadmap packet unless structure is actually blocking it ## Current execution-reality references diff --git a/docs/arch/HYPERTWIST_PHASE4_DASHBOARD_LIVE_GUIDANCE_DECK_PREVIEW_HANDOFF_RECOMMENDATION_INSPECT_SURFACE_PACKET_2026-05-09.md b/docs/arch/HYPERTWIST_PHASE4_DASHBOARD_LIVE_GUIDANCE_DECK_PREVIEW_HANDOFF_RECOMMENDATION_INSPECT_SURFACE_PACKET_2026-05-09.md new file mode 100644 index 0000000..d0782bf --- /dev/null +++ b/docs/arch/HYPERTWIST_PHASE4_DASHBOARD_LIVE_GUIDANCE_DECK_PREVIEW_HANDOFF_RECOMMENDATION_INSPECT_SURFACE_PACKET_2026-05-09.md @@ -0,0 +1,54 @@ +# HyperTwist Phase 4 dashboard live guidance deck preview and handoff recommendation inspect surface packet + +- bounded Phase `4` dashboard-consumer slice +- date: `2026-05-09` + +## Intent + +Stay on the deliberate live `UHyperTwistCoachDashboardWidget` guidance-consumer lane and expose the remaining surfaced recommended/follow-up deck preview state and live coach handoff recommendation state directly to gameplay and Blueprint callers. + +## Why this packet exists + +The dashboard already derived: + +- the displayed recommended guidance deck preview line +- the displayed follow-up guidance deck preview line +- the current coach handoff recommendation line and retained recommendation detail posture + +But that live state still only surfaced through dashboard text blocks. Callers still had to scrape widget presentation or reopen deck-preview / handoff derivation to inspect what the dashboard was currently showing. + +## What changed + +- added `FHyperTwistTrainingCoachDashboardGuidanceDeckPreviewInspectSurface` +- added `FHyperTwistTrainingCoachDashboardHandoffRecommendationInspectSurface` +- added `GetDisplayedRecommendedGuidanceDeckPreviewInspectSurface()` +- added `GetDisplayedFollowUpGuidanceDeckPreviewInspectSurface()` +- added `GetDisplayedCoachHandoffRecommendationInspectSurface()` +- retained the exact displayed preview / recommendation lines inside the widget so the new inspect surfaces report the same live strings the dashboard is already presenting +- kept the packet bounded to `UHyperTwistCoachDashboardWidget` plus shared surface types, with no dashboard redesign and no backend continuity changes + +## Files + +- `C:\HyperTwist\UnrealHyperTwist\Source\UnrealHyperTwist\Public\HyperTwistTraining\HyperTwistTrainingTypes.h` +- `C:\HyperTwist\UnrealHyperTwist\Source\UnrealHyperTwist\Public\HyperTwistTraining\HyperTwistCoachDashboardWidget.h` +- `C:\HyperTwist\UnrealHyperTwist\Source\UnrealHyperTwist\Private\HyperTwistTraining\HyperTwistCoachDashboardWidget.cpp` +- `C:\HyperTwist\docs\HYPERTWIST_ROADMAP_OVERHAUL_EXPANSION_GUIDE.md` + +## Outcome + +Gameplay and Blueprint callers can now inspect: + +- the displayed recommended guidance deck preview, including deck identity, selection posture, brief fallback, and preview-case summary +- the displayed follow-up guidance deck preview, including deck identity, selection posture, brief fallback, and preview-case summary +- the live coach handoff recommendation, including readiness posture, retained recommendation source posture, and current accepted-packet / method-drill / exact-branch reinforcement flags + +without scraping dashboard text blocks or reopening deck-preview / handoff derivation. + +## Validation + +- full `Development Editor | Win64` MSBuild on `UnrealHyperTwist.sln` + - passed with `0 Warning(s), 0 Error(s)` + +## Next clean slice + +Stay on the same live dashboard guidance-consumer lane and land the displayed coach handoff status, displayed transition status, and displayed continuation / queued / follow-up / recommended run action-label inspect surface group.