Expose dashboard deck preview and handoff recommendation surfaces

This commit is contained in:
axiomlogicnexus 2026-05-09 19:36:08 +02:00
parent 8467b00225
commit 5ab380abfe
5 changed files with 828 additions and 2 deletions

View file

@ -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<FString> 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<FString> 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<FString> 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<FString> 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),

View file

@ -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;

View file

@ -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
{

View file

@ -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

View file

@ -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.