From 9fafb7d5c721e5dfeb4bb38e64c95d3ce27b7afe Mon Sep 17 00:00:00 2001 From: axiomlogicnexus Date: Sun, 10 May 2026 16:08:13 +0200 Subject: [PATCH] Expose dashboard template-launch and preview action control surfaces --- .../HyperTwistCoachDashboardWidget.cpp | 266 +++++++++++++++++- .../HyperTwistCoachDashboardWidget.h | 17 ++ .../HyperTwistTrainingTypes.h | 173 ++++++++++++ ...RTWIST_ROADMAP_OVERHAUL_EXPANSION_GUIDE.md | 4 +- ...NTROL_INSPECT_SURFACE_PACKET_2026-05-10.md | 60 ++++ 5 files changed, 515 insertions(+), 5 deletions(-) create mode 100644 docs/arch/HYPERTWIST_PHASE4_DASHBOARD_LIVE_TEMPLATE_LAUNCH_RECOMMENDED_FOLLOW_UP_PREVIEW_ACTION_CONTROL_INSPECT_SURFACE_PACKET_2026-05-10.md diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistCoachDashboardWidget.cpp b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistCoachDashboardWidget.cpp index a0bb772..1c4a77d 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistCoachDashboardWidget.cpp +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistCoachDashboardWidget.cpp @@ -12683,6 +12683,259 @@ UHyperTwistCoachDashboardWidget::GetDisplayedRunRecapActionControlInspectSurface return Surface; } +FHyperTwistTrainingCoachDashboardPreferredTemplateLaunchActionInspectSurface +UHyperTwistCoachDashboardWidget::GetDisplayedPreferredTemplateLaunchActionInspectSurface() const +{ + FHyperTwistTrainingCoachDashboardPreferredTemplateLaunchActionInspectSurface Surface; + Surface.Headline = TEXT("Preferred template launch action"); + + const bool bHasActiveRun = CachedRunState.Session.IsStructurallyValid() + && CachedRunState.ActiveDeck.IsStructurallyValid(); + FHyperTwistTrainingTemplateLaunchSelectionProvenance SelectionProvenance; + const FHyperTwistTrainingSessionTemplate* PreferredTemplateLaunch = + FindPreferredDashboardTemplateLaunch(&SelectionProvenance); + int32 EligibleTemplateCount = 0; + for (const FHyperTwistTrainingSessionTemplate& SessionTemplate : CachedActiveTrainingSessionTemplates) + { + if (HyperTwistCoachDashboardWidgetInternal::ScoreTemplateForDashboardLaunch(SessionTemplate) > 0) + { + EligibleTemplateCount += 1; + } + } + + const bool bHasPreferredTemplate = PreferredTemplateLaunch != nullptr; + const bool bUsesReviewLane = + bHasPreferredTemplate + && (PreferredTemplateLaunch->bPreferReviewRun + || PreferredTemplateLaunch->TemplateKind == EHyperTwistTrainingSessionTemplateKind::ReviewPlan + || PreferredTemplateLaunch->TemplateKind == EHyperTwistTrainingSessionTemplateKind::Coach); + const FString PreferredTemplateTitle = bHasPreferredTemplate + ? (PreferredTemplateLaunch->Title.IsEmpty() + ? PreferredTemplateLaunch->TemplateId + : PreferredTemplateLaunch->Title) + : TEXT("n/a"); + const FString PreferredTemplateUserId = bHasPreferredTemplate + ? (PreferredTemplateLaunch->UserId.IsEmpty() ? DefaultUserId : PreferredTemplateLaunch->UserId) + : FString(); + const FHyperTwistTrainingTemplateLaunchScopedStats* PreferredTemplateStats = + bHasPreferredTemplate + ? FindCachedTemplateLaunchScopedStats( + PreferredTemplateLaunch->TemplateId, + PreferredTemplateUserId) + : nullptr; + const FString TemplateSelectionDetail = + HyperTwistCoachDashboardWidgetInternal::BuildTemplateLaunchSelectionDetailFragment( + SelectionProvenance.IsStructurallyValid() ? &SelectionProvenance : nullptr + ); + const bool bCanLaunchPreferredTemplate = + !bHasLiveAttemptTimer && !bHasActiveRun && bHasPreferredTemplate; + + FString TemplateActionLabel = TEXT("No Template"); + if (bHasPreferredTemplate) + { + TemplateActionLabel = + PreferredTemplateTitle.Len() <= 28 + ? PreferredTemplateTitle + : (bUsesReviewLane ? TEXT("Launch Review Template") : TEXT("Launch Recovery Template")); + } + if (bHasLiveAttemptTimer && bHasPreferredTemplate) + { + TemplateActionLabel = TEXT("Template Busy"); + } + else if (bHasActiveRun && bHasPreferredTemplate) + { + TemplateActionLabel = TEXT("Clear Run For Template"); + } + + Surface.ActionLabel = !DisplayedTemplateLaunchActionLabel.IsEmpty() + ? DisplayedTemplateLaunchActionLabel + : TemplateActionLabel; + Surface.DisplayedTemplateStatusLine = !DisplayedTemplateLaunchStatusLine.IsEmpty() + ? DisplayedTemplateLaunchStatusLine + : TEXT("Template launch status: not yet displayed."); + Surface.DisplayedTemplateDetailLine = !DisplayedTemplateLaunchDetailLine.IsEmpty() + ? DisplayedTemplateLaunchDetailLine + : TEXT("Template launch detail: not yet displayed."); + Surface.TemplateTitle = PreferredTemplateTitle; + Surface.TemplateId = bHasPreferredTemplate && !PreferredTemplateLaunch->TemplateId.IsEmpty() + ? PreferredTemplateLaunch->TemplateId + : TEXT("n/a"); + Surface.TemplateKindLabel = bHasPreferredTemplate + ? HyperTwistCoachDashboardWidgetInternal::EnumDisplayName(PreferredTemplateLaunch->TemplateKind) + : TEXT("n/a"); + Surface.UserId = PreferredTemplateUserId.IsEmpty() ? TEXT("n/a") : PreferredTemplateUserId; + Surface.FocusDeckId = + bHasPreferredTemplate && !PreferredTemplateLaunch->FocusDeckId.IsEmpty() + ? PreferredTemplateLaunch->FocusDeckId + : TEXT("n/a"); + Surface.GuidanceLabel = + bHasPreferredTemplate && !PreferredTemplateLaunch->GuidanceLabel.IsEmpty() + ? PreferredTemplateLaunch->GuidanceLabel + : TEXT("n/a"); + Surface.SuggestedModeLabel = bHasPreferredTemplate + ? HyperTwistCoachDashboardWidgetInternal::EnumDisplayName(PreferredTemplateLaunch->SuggestedMode) + : TEXT("n/a"); + Surface.SuggestedSelectionLabel = bHasPreferredTemplate + ? HyperTwistCoachDashboardWidgetInternal::EnumDisplayName( + PreferredTemplateLaunch->SuggestedSelectionPolicy) + : TEXT("n/a"); + Surface.SelectionDetailLine = + TemplateSelectionDetail.IsEmpty() ? TEXT("none") : TemplateSelectionDetail; + Surface.EligibleTemplateCount = EligibleTemplateCount; + Surface.ActiveTemplateCount = CachedActiveTrainingSessionTemplates.Num(); + Surface.RecommendedCaseCount = + bHasPreferredTemplate ? PreferredTemplateLaunch->RecommendedCaseCount : 0; + Surface.FocusCaseCount = bHasPreferredTemplate ? PreferredTemplateLaunch->FocusCaseIds.Num() : 0; + Surface.bHasPreferredTemplate = bHasPreferredTemplate; + Surface.bUsesReviewLane = bUsesReviewLane; + Surface.bHasScopedStats = + PreferredTemplateStats != nullptr && PreferredTemplateStats->IsStructurallyValid(); + Surface.bHasSelectionProvenance = SelectionProvenance.IsStructurallyValid(); + Surface.bHasLiveAttemptTimer = bHasLiveAttemptTimer; + Surface.bHasActiveRun = bHasActiveRun; + Surface.bActionEnabled = bCanLaunchPreferredTemplate; + Surface.StatusLine = FString::Printf( + TEXT("%s | Action: %s (%s)"), + *Surface.DisplayedTemplateStatusLine, + *Surface.ActionLabel, + Surface.bActionEnabled ? TEXT("enabled") : TEXT("disabled")); + Surface.DetailLine = FString::Printf( + TEXT("%s | active run %s | live timer %s | review lane %s | selection provenance %s"), + *Surface.DisplayedTemplateDetailLine, + Surface.bHasActiveRun ? TEXT("yes") : TEXT("no"), + Surface.bHasLiveAttemptTimer ? TEXT("yes") : TEXT("no"), + Surface.bUsesReviewLane ? TEXT("yes") : TEXT("no"), + Surface.bHasSelectionProvenance ? TEXT("yes") : TEXT("no")); + return Surface; +} + +FHyperTwistTrainingCoachDashboardGuidancePreviewActionInspectSurface +UHyperTwistCoachDashboardWidget::GetDisplayedRecommendedPreviewActionInspectSurface() const +{ + FHyperTwistTrainingCoachDashboardGuidancePreviewActionInspectSurface Surface; + Surface.Headline = TEXT("Recommended preview action"); + Surface.GuidanceLaneLabel = TEXT("recommended"); + + const auto BuildPreviewLaneLabel = [](const EHyperTwistCoachDashboardGuidancePreviewLane Lane) -> FString + { + return Lane == EHyperTwistCoachDashboardGuidancePreviewLane::Recommended + ? TEXT("recommended") + : (Lane == EHyperTwistCoachDashboardGuidancePreviewLane::FollowUp + ? TEXT("follow-up") + : TEXT("none")); + }; + + const FHyperTwistTrainingCoachDashboardGuidanceDeckPreviewInspectSurface PreviewSurface = + GetDisplayedRecommendedGuidanceDeckPreviewInspectSurface(); + const bool bActionEnabled = + !bHasLiveAttemptTimer && CachedCoachPanelState.bCanStartCoachRecommendedRun; + + Surface.ActionLabel = !DisplayedPreviewRecommendedActionLabel.IsEmpty() + ? DisplayedPreviewRecommendedActionLabel + : TEXT("Preview Recommended"); + Surface.PreviewStatusLine = PreviewSurface.StatusLine.IsEmpty() + ? TEXT("Recommended guidance: no guidance available.") + : PreviewSurface.StatusLine; + Surface.DeckLabel = PreviewSurface.DeckLabel.IsEmpty() ? TEXT("n/a") : PreviewSurface.DeckLabel; + Surface.BriefHeadline = + PreviewSurface.BriefHeadline.IsEmpty() ? TEXT("n/a") : PreviewSurface.BriefHeadline; + Surface.ActivePreviewLaneLabel = BuildPreviewLaneLabel(ActiveGuidancePreviewLane); + Surface.FocusDeckId = + PreviewSurface.FocusDeckId.IsEmpty() ? TEXT("n/a") : PreviewSurface.FocusDeckId; + Surface.FocusMethodSegmentId = PreviewSurface.FocusMethodSegmentId.IsEmpty() + ? TEXT("n/a") + : PreviewSurface.FocusMethodSegmentId; + Surface.CaseCount = PreviewSurface.CaseCount; + Surface.FocusCaseCount = PreviewSurface.FocusCaseCount; + Surface.bGuidanceReadyToLaunch = PreviewSurface.bGuidanceReadyToLaunch; + Surface.bHasGuidancePayload = + PreviewSurface.bHasDeck || PreviewSurface.bHasBrief || PreviewSurface.bGuidanceReadyToLaunch; + Surface.bHasDeck = PreviewSurface.bHasDeck; + Surface.bHasBrief = PreviewSurface.bHasBrief; + Surface.bUsesBriefFallback = PreviewSurface.bUsesBriefFallback; + Surface.bHasLiveAttemptTimer = bHasLiveAttemptTimer; + Surface.bActionEnabled = bActionEnabled; + Surface.bActivePreviewLane = PreviewSurface.bActivePreviewLane; + Surface.bClosedLoopIdleDashboard = PreviewSurface.bClosedLoopIdleDashboard; + Surface.StatusLine = FString::Printf( + TEXT("%s | Action: %s (%s)"), + *Surface.PreviewStatusLine, + *Surface.ActionLabel, + Surface.bActionEnabled ? TEXT("enabled") : TEXT("disabled")); + Surface.DetailLine = FString::Printf( + TEXT("%s | live timer %s | active preview lane %s"), + *PreviewSurface.DetailLine, + Surface.bHasLiveAttemptTimer ? TEXT("yes") : TEXT("no"), + *Surface.ActivePreviewLaneLabel); + return Surface; +} + +FHyperTwistTrainingCoachDashboardGuidancePreviewActionInspectSurface +UHyperTwistCoachDashboardWidget::GetDisplayedFollowUpPreviewActionInspectSurface() const +{ + FHyperTwistTrainingCoachDashboardGuidancePreviewActionInspectSurface Surface; + Surface.Headline = TEXT("Follow-up preview action"); + Surface.GuidanceLaneLabel = TEXT("follow-up"); + + const auto BuildPreviewLaneLabel = [](const EHyperTwistCoachDashboardGuidancePreviewLane Lane) -> FString + { + return Lane == EHyperTwistCoachDashboardGuidancePreviewLane::Recommended + ? TEXT("recommended") + : (Lane == EHyperTwistCoachDashboardGuidancePreviewLane::FollowUp + ? TEXT("follow-up") + : TEXT("none")); + }; + + const FHyperTwistTrainingCoachDashboardGuidanceDeckPreviewInspectSurface PreviewSurface = + GetDisplayedFollowUpGuidanceDeckPreviewInspectSurface(); + const bool bActionEnabled = + !bHasLiveAttemptTimer + && CachedCoachPanelState.bCanStartCoachFollowUpRun + && CachedCoachPanelState.bHasFollowUpGuidance; + + Surface.ActionLabel = !DisplayedPreviewFollowUpActionLabel.IsEmpty() + ? DisplayedPreviewFollowUpActionLabel + : TEXT("Preview Follow-Up"); + Surface.PreviewStatusLine = PreviewSurface.StatusLine.IsEmpty() + ? TEXT("Follow-up guidance: no guidance available.") + : PreviewSurface.StatusLine; + Surface.DeckLabel = PreviewSurface.DeckLabel.IsEmpty() ? TEXT("n/a") : PreviewSurface.DeckLabel; + Surface.BriefHeadline = + PreviewSurface.BriefHeadline.IsEmpty() ? TEXT("n/a") : PreviewSurface.BriefHeadline; + Surface.ActivePreviewLaneLabel = BuildPreviewLaneLabel(ActiveGuidancePreviewLane); + Surface.FocusDeckId = + PreviewSurface.FocusDeckId.IsEmpty() ? TEXT("n/a") : PreviewSurface.FocusDeckId; + Surface.FocusMethodSegmentId = PreviewSurface.FocusMethodSegmentId.IsEmpty() + ? TEXT("n/a") + : PreviewSurface.FocusMethodSegmentId; + Surface.CaseCount = PreviewSurface.CaseCount; + Surface.FocusCaseCount = PreviewSurface.FocusCaseCount; + Surface.bGuidanceReadyToLaunch = PreviewSurface.bGuidanceReadyToLaunch; + Surface.bHasGuidancePayload = + CachedCoachPanelState.bHasFollowUpGuidance + || PreviewSurface.bHasDeck + || PreviewSurface.bHasBrief + || PreviewSurface.bGuidanceReadyToLaunch; + Surface.bHasDeck = PreviewSurface.bHasDeck; + Surface.bHasBrief = PreviewSurface.bHasBrief; + Surface.bUsesBriefFallback = PreviewSurface.bUsesBriefFallback; + Surface.bHasLiveAttemptTimer = bHasLiveAttemptTimer; + Surface.bActionEnabled = bActionEnabled; + Surface.bActivePreviewLane = PreviewSurface.bActivePreviewLane; + Surface.bClosedLoopIdleDashboard = PreviewSurface.bClosedLoopIdleDashboard; + Surface.StatusLine = FString::Printf( + TEXT("%s | Action: %s (%s)"), + *Surface.PreviewStatusLine, + *Surface.ActionLabel, + Surface.bActionEnabled ? TEXT("enabled") : TEXT("disabled")); + Surface.DetailLine = FString::Printf( + TEXT("%s | live timer %s | active preview lane %s"), + *PreviewSurface.DetailLine, + Surface.bHasLiveAttemptTimer ? TEXT("yes") : TEXT("no"), + *Surface.ActivePreviewLaneLabel); + return Surface; +} + FHyperTwistTrainingCoachDashboardActiveAttemptTimerStatusInspectSurface UHyperTwistCoachDashboardWidget::GetDisplayedActiveAttemptTimerStatusInspectSurface() const { @@ -25851,6 +26104,8 @@ void UHyperTwistCoachDashboardWidget::UpdateDashboardPresentation() { CoachHandoffRecommendationDetailTextBlock->SetText(FText::FromString(CoachHandoffRecommendationDetailLine)); } + DisplayedTemplateLaunchStatusLine = TemplateLaunchStatusLine; + DisplayedTemplateLaunchDetailLine = TemplateLaunchDetailLine; if (TemplateLaunchHeaderTextBlock != nullptr) { TemplateLaunchHeaderTextBlock->SetText(FText::FromString(TEXT("[Template Launch]"))); @@ -26062,17 +26317,20 @@ void UHyperTwistCoachDashboardWidget::UpdateDashboardPresentation() { TemplateLaunchButtonLabel = TEXT("Clear Run For Template"); } + DisplayedTemplateLaunchActionLabel = TemplateLaunchButtonLabel; if (StartTemplateLaunchButtonLabel != nullptr) { - StartTemplateLaunchButtonLabel->SetText(FText::FromString(TemplateLaunchButtonLabel)); + StartTemplateLaunchButtonLabel->SetText(FText::FromString(DisplayedTemplateLaunchActionLabel)); } if (StartTemplateLaunchButton != nullptr) { StartTemplateLaunchButton->SetIsEnabled(bCanLaunchPreferredTemplate); } + DisplayedPreviewRecommendedActionLabel = TEXT("Preview Recommended"); if (PreviewRecommendedButtonLabel != nullptr) { - PreviewRecommendedButtonLabel->SetText(FText::FromString(TEXT("Preview Recommended"))); + PreviewRecommendedButtonLabel->SetText(FText::FromString( + DisplayedPreviewRecommendedActionLabel)); } if (PreviewRecommendedButton != nullptr) { @@ -26080,9 +26338,11 @@ void UHyperTwistCoachDashboardWidget::UpdateDashboardPresentation() !bHasLiveAttemptTimer && CachedCoachPanelState.bCanStartCoachRecommendedRun ); } + DisplayedPreviewFollowUpActionLabel = TEXT("Preview Follow-Up"); if (PreviewFollowUpButtonLabel != nullptr) { - PreviewFollowUpButtonLabel->SetText(FText::FromString(TEXT("Preview Follow-Up"))); + PreviewFollowUpButtonLabel->SetText(FText::FromString( + DisplayedPreviewFollowUpActionLabel)); } if (PreviewFollowUpButton != nullptr) { diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistCoachDashboardWidget.h b/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistCoachDashboardWidget.h index 2d28f84..864bda8 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistCoachDashboardWidget.h +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistCoachDashboardWidget.h @@ -887,6 +887,18 @@ public: FHyperTwistTrainingCoachDashboardRunRecapActionControlInspectSurface GetDisplayedRunRecapActionControlInspectSurface() const; + UFUNCTION(BlueprintPure, Category = "HyperTwist|Training|Coach|Dashboard") + FHyperTwistTrainingCoachDashboardPreferredTemplateLaunchActionInspectSurface + GetDisplayedPreferredTemplateLaunchActionInspectSurface() const; + + UFUNCTION(BlueprintPure, Category = "HyperTwist|Training|Coach|Dashboard") + FHyperTwistTrainingCoachDashboardGuidancePreviewActionInspectSurface + GetDisplayedRecommendedPreviewActionInspectSurface() const; + + UFUNCTION(BlueprintPure, Category = "HyperTwist|Training|Coach|Dashboard") + FHyperTwistTrainingCoachDashboardGuidancePreviewActionInspectSurface + GetDisplayedFollowUpPreviewActionInspectSurface() const; + UFUNCTION(BlueprintPure, Category = "HyperTwist|Training|Coach|Dashboard") FHyperTwistTrainingCoachDashboardActiveAttemptTimerStatusInspectSurface GetDisplayedActiveAttemptTimerStatusInspectSurface() const; @@ -2026,8 +2038,11 @@ private: FString DisplayedTransitionStatusLine; FString DisplayedActiveRunStatusLine; FString DisplayedActiveRunSummaryLine; + FString DisplayedTemplateLaunchStatusLine; + FString DisplayedTemplateLaunchDetailLine; FString DisplayedRunRecapStatusLine; FString DisplayedRunRecapDetailLine; + FString DisplayedTemplateLaunchActionLabel; FString DisplayedRepeatRunActionLabel; FString DisplayedDismissRecapActionLabel; FString DisplayedCurrentCasePromptLine; @@ -2049,6 +2064,8 @@ private: FString DisplayedAttemptInputStatusLine; FString DisplayedSplitCaptureStatusLine; FString DisplayedTimerStatusLine; + FString DisplayedPreviewRecommendedActionLabel; + FString DisplayedPreviewFollowUpActionLabel; FString DisplayedRecommendedDeckPreviewLine; FString DisplayedFollowUpDeckPreviewLine; FString DisplayedMethodDrillDiagnosticsStatusLine; diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingTypes.h b/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingTypes.h index 5b5acc8..c02db10 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingTypes.h +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingTypes.h @@ -13536,6 +13536,179 @@ struct FHyperTwistTrainingCoachDashboardRunRecapActionControlInspectSurface } }; +USTRUCT(BlueprintType) +struct FHyperTwistTrainingCoachDashboardPreferredTemplateLaunchActionInspectSurface +{ + 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 ActionLabel; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString DisplayedTemplateStatusLine; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString DisplayedTemplateDetailLine; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString TemplateTitle; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString TemplateId; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString TemplateKindLabel; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString UserId; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString FocusDeckId; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString GuidanceLabel; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString SuggestedModeLabel; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString SuggestedSelectionLabel; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString SelectionDetailLine; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + int32 EligibleTemplateCount = 0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + int32 ActiveTemplateCount = 0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + int32 RecommendedCaseCount = 0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + int32 FocusCaseCount = 0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bHasPreferredTemplate = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bUsesReviewLane = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bHasScopedStats = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bHasSelectionProvenance = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bHasLiveAttemptTimer = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bHasActiveRun = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bActionEnabled = false; + + bool IsStructurallyValid() const + { + return !StatusLine.IsEmpty() + || !DetailLine.IsEmpty() + || bHasPreferredTemplate + || bActionEnabled; + } +}; + +USTRUCT(BlueprintType) +struct FHyperTwistTrainingCoachDashboardGuidancePreviewActionInspectSurface +{ + 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 ActionLabel; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString GuidanceLaneLabel; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString PreviewStatusLine; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString DeckLabel; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString BriefHeadline; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString ActivePreviewLaneLabel; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString FocusDeckId; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString FocusMethodSegmentId; + + 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 bHasGuidancePayload = 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 bHasLiveAttemptTimer = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bActionEnabled = 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() + || bGuidanceReadyToLaunch + || bHasGuidancePayload + || bActionEnabled; + } +}; + USTRUCT(BlueprintType) struct FHyperTwistTrainingCoachDashboardActiveAttemptTimerStatusInspectSurface { diff --git a/docs/HYPERTWIST_ROADMAP_OVERHAUL_EXPANSION_GUIDE.md b/docs/HYPERTWIST_ROADMAP_OVERHAUL_EXPANSION_GUIDE.md index 15de528..eb9d4b6 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 adjacent in that same live dashboard consumer lane and adds the run-recap action control inspect surface over the already-retained recap action labels and enablement posture, so gameplay/Blueprint callers can inspect the exact surfaced repeat/dismiss labels and their gating state without scraping widget button text - - the next best clean move is to stay adjacent in that same live dashboard consumer lane and expose the preferred template-launch plus recommended/follow-up preview action control surface trio over the already-derived launch/preview button labels and enablement posture + - the latest landed bounded `Phase 4` packet stays adjacent in that same live dashboard consumer lane and adds the preferred template-launch plus recommended/follow-up preview action control surface trio over the already-derived launch/preview button labels and enablement posture, so gameplay/Blueprint callers can inspect the exact surfaced template/preview action labels and their gating state without scraping widget button text + - the next best clean move is to stay adjacent in that same live dashboard consumer lane and expose the guidance preview case navigation control surface pair over the already-derived `Prev Guidance Case` / `Next Guidance Case` labels and enablement posture - 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 ## Donor portfolio phase taxonomy diff --git a/docs/arch/HYPERTWIST_PHASE4_DASHBOARD_LIVE_TEMPLATE_LAUNCH_RECOMMENDED_FOLLOW_UP_PREVIEW_ACTION_CONTROL_INSPECT_SURFACE_PACKET_2026-05-10.md b/docs/arch/HYPERTWIST_PHASE4_DASHBOARD_LIVE_TEMPLATE_LAUNCH_RECOMMENDED_FOLLOW_UP_PREVIEW_ACTION_CONTROL_INSPECT_SURFACE_PACKET_2026-05-10.md new file mode 100644 index 0000000..79e537c --- /dev/null +++ b/docs/arch/HYPERTWIST_PHASE4_DASHBOARD_LIVE_TEMPLATE_LAUNCH_RECOMMENDED_FOLLOW_UP_PREVIEW_ACTION_CONTROL_INSPECT_SURFACE_PACKET_2026-05-10.md @@ -0,0 +1,60 @@ +# HyperTwist Phase 4 dashboard live template-launch and preview action control inspect surface packet + +- bounded Phase `4` dashboard-consumer slice +- date: `2026-05-10` + +## Intent + +Stay adjacent in the live dashboard consumer lane and expose the preferred template-launch plus recommended/follow-up preview action controls directly to gameplay and Blueprint callers. + +## Why this packet exists + +`UHyperTwistCoachDashboardWidget` already displayed: + +- the preferred template-launch action label +- the recommended preview action label +- the follow-up preview action label +- the enablement posture behind those three controls + +But that control band still only surfaced through button labels and `SetIsEnabled` state. Callers still had to scrape widget controls to understand whether the dashboard was willing to launch the preferred template, whether recommended guidance preview was currently actionable, and whether follow-up guidance preview was currently blocked by timer or guidance availability. + +## What changed + +- added `FHyperTwistTrainingCoachDashboardPreferredTemplateLaunchActionInspectSurface` +- added `FHyperTwistTrainingCoachDashboardGuidancePreviewActionInspectSurface` +- added `GetDisplayedPreferredTemplateLaunchActionInspectSurface()` +- added `GetDisplayedRecommendedPreviewActionInspectSurface()` +- added `GetDisplayedFollowUpPreviewActionInspectSurface()` +- retained the exact displayed template-launch, recommended-preview, and follow-up-preview action labels inside the widget so the new surfaces report the same live strings the dashboard is already presenting +- retained the exact displayed template-launch status/detail lines inside the widget so the preferred-template action surface reports the same live template band the dashboard is already presenting +- kept the packet bounded to `UHyperTwistCoachDashboardWidget` plus shared surface types, with no template-selection refactor 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 exact displayed preferred template-launch action label +- the exact displayed recommended preview action label +- the exact displayed follow-up preview action label +- whether each of those three actions is currently enabled +- the displayed template-launch status/detail band aligned with the preferred template action +- whether recommended or follow-up preview is currently backed by a live deck, a brief fallback, or neither +- which preview lane is currently active behind those controls + +without scraping widget button text or reopening launch/preview gating logic. + +## 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 consumer lane and expose the guidance preview case navigation control surface pair over the already-derived `Prev Guidance Case` / `Next Guidance Case` labels and enablement posture.