From 67ec0915cbd362723dadde9d953967dfbddadcec Mon Sep 17 00:00:00 2001 From: axiomlogicnexus Date: Sat, 9 May 2026 19:52:11 +0200 Subject: [PATCH] Expose dashboard handoff, transition, and action-label surfaces --- .../HyperTwistCoachDashboardWidget.cpp | 346 ++++++++++++++++++ .../HyperTwistCoachDashboardWidget.h | 18 + .../HyperTwistTrainingTypes.h | 217 +++++++++++ ...RTWIST_ROADMAP_OVERHAUL_EXPANSION_GUIDE.md | 4 +- ...LABEL_INSPECT_SURFACE_PACKET_2026-05-09.md | 55 +++ 5 files changed, 638 insertions(+), 2 deletions(-) create mode 100644 docs/arch/HYPERTWIST_PHASE4_DASHBOARD_LIVE_HANDOFF_STATUS_TRANSITION_STATUS_RUN_ACTION_LABEL_INSPECT_SURFACE_PACKET_2026-05-09.md diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistCoachDashboardWidget.cpp b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistCoachDashboardWidget.cpp index d51b181..4cbb828 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistCoachDashboardWidget.cpp +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistCoachDashboardWidget.cpp @@ -10385,6 +10385,346 @@ UHyperTwistCoachDashboardWidget::GetDisplayedCoachHandoffRecommendationInspectSu return Surface; } +FHyperTwistTrainingCoachDashboardHandoffStatusInspectSurface +UHyperTwistCoachDashboardWidget::GetDisplayedCoachHandoffStatusInspectSurface() const +{ + FHyperTwistTrainingCoachDashboardHandoffStatusInspectSurface Surface; + Surface.Headline = TEXT("Coach handoff status"); + + const bool bHasActiveRun = CachedRunState.Session.IsStructurallyValid() + && CachedRunState.ActiveDeck.IsStructurallyValid(); + Surface.bHasLiveAttemptTimer = bHasLiveAttemptTimer; + Surface.bHasActiveRun = bHasActiveRun; + Surface.bQueueReady = CachedCoachPanelState.bCanStartQueuedRun; + Surface.bFollowUpReady = + CachedCoachPanelState.bCanStartCoachFollowUpRun + && CachedCoachPanelState.bHasFollowUpGuidance; + Surface.bRecommendedReady = CachedCoachPanelState.bCanStartCoachRecommendedRun; + Surface.QueueSourceLabel = + CachedCoachPanelState.NextQueueSourceLabel.IsEmpty() + ? TEXT("n/a") + : CachedCoachPanelState.NextQueueSourceLabel; + Surface.FollowUpBriefHeadline = + CachedCoachFollowUpBrief.Headline.IsEmpty() ? TEXT("n/a") : CachedCoachFollowUpBrief.Headline; + Surface.RecommendedBriefHeadline = + CachedCoachBrief.Headline.IsEmpty() ? TEXT("n/a") : CachedCoachBrief.Headline; + Surface.FollowUpFocusCaseCount = CachedCoachFollowUpBrief.FocusCaseIds.Num(); + Surface.RecommendedFocusCaseCount = CachedCoachBrief.FocusCaseIds.Num(); + Surface.QueueStatusLabel = Surface.bQueueReady + ? FString::Printf(TEXT("queue ready from %s"), *Surface.QueueSourceLabel) + : TEXT("queue not ready"); + Surface.FollowUpStatusLabel = Surface.bFollowUpReady + ? FString::Printf( + TEXT("follow-up %s (%d cases)"), + CachedCoachFollowUpBrief.Headline.IsEmpty() ? TEXT("ready") : *CachedCoachFollowUpBrief.Headline, + Surface.FollowUpFocusCaseCount) + : TEXT("follow-up not ready"); + Surface.RecommendedStatusLabel = Surface.bRecommendedReady + ? FString::Printf( + TEXT("recommended %s (%d cases)"), + CachedCoachBrief.Headline.IsEmpty() ? TEXT("ready") : *CachedCoachBrief.Headline, + Surface.RecommendedFocusCaseCount) + : TEXT("recommended not ready"); + + const bool bQueueRecoveryMemorySource = + HyperTwistCoachDashboardWidgetInternal::IsQueueRecoveryMemorySource( + CachedCoachPanelState.PreferredGuidanceSourceLabel); + Surface.bClosedLoopIdleDashboard = + IsCoachLoopActionabilityClosed() + && ( + bQueueRecoveryMemorySource + || !CachedCoachPanelState.PreferredGuidanceSourceLabel.IsEmpty() + || DisplayedCoachHandoffRecommendationKind != EHyperTwistTrainingCoachHandoffKind::None); + + Surface.StatusLine = !DisplayedCoachHandoffStatusLine.IsEmpty() + ? DisplayedCoachHandoffStatusLine + : FString::Printf( + TEXT("Coach handoff: %s | %s | %s"), + *Surface.QueueStatusLabel, + *Surface.FollowUpStatusLabel, + *Surface.RecommendedStatusLabel); + if (Surface.bClosedLoopIdleDashboard) + { + Surface.DetailLine = + TEXT("Coach handoff detail: retained readiness posture remains inspectable, but no queued, follow-up, or recommended continuation is active in the closed-loop idle state."); + } + else if (Surface.bHasLiveAttemptTimer) + { + Surface.DetailLine = + TEXT("Coach handoff detail: live attempt or inspection is active, so coach-directed handoff launch surfaces are intentionally paused."); + } + else if (Surface.bHasActiveRun) + { + Surface.DetailLine = + TEXT("Coach handoff detail: an active run is still owned by the dashboard; finish, clear, or complete it before launching another coach-directed run."); + } + else + { + Surface.DetailLine = FString::Printf( + TEXT("Coach handoff detail: %s | %s | %s"), + *Surface.QueueStatusLabel, + *Surface.FollowUpStatusLabel, + *Surface.RecommendedStatusLabel); + } + return Surface; +} + +FHyperTwistTrainingCoachDashboardTransitionStatusInspectSurface +UHyperTwistCoachDashboardWidget::GetDisplayedTransitionStatusInspectSurface() const +{ + FHyperTwistTrainingCoachDashboardTransitionStatusInspectSurface Surface; + Surface.Headline = TEXT("Transition"); + + const bool bHasActiveRun = CachedRunState.Session.IsStructurallyValid() + && CachedRunState.ActiveDeck.IsStructurallyValid(); + const FString ActiveCaseId = CachedRunState.CurrentSelection.TrainingCase.CaseId.IsEmpty() + ? TEXT("n/a") + : CachedRunState.CurrentSelection.TrainingCase.CaseId; + const FString ActivePromptLabel = + CachedRunState.CurrentSelection.TrainingCase.PromptLabel.IsEmpty() + ? ActiveCaseId + : CachedRunState.CurrentSelection.TrainingCase.PromptLabel; + const bool bLiveInspectionPhase = IsLiveAttemptInspectionPhase(); + const bool bLiveSolvePhase = bHasLiveAttemptTimer + && LiveAttemptPhase == EHyperTwistCoachDashboardAttemptPhase::Solving; + const bool bHasSessionScopedLastStep = !LastStepResult.SessionSummary.TrainingSessionId.IsEmpty() + && LastStepResult.SessionSummary.TrainingSessionId == CachedRunState.Session.TrainingSessionId; + const bool bLastStepCompletedRun = bHasSessionScopedLastStep + && (LastStepResult.AttemptResolution.UpdatedSession.SessionState + == EHyperTwistTrainingSessionState::Completed + || LastStepResult.AttemptResolution.UpdatedSession.SessionState + == EHyperTwistTrainingSessionState::Aborted); + const bool bHasNextCaseFromLastStep = bHasSessionScopedLastStep + && LastStepResult.AttemptResolution.NextCaseSelection.IsStructurallyValid(); + const bool bGeneratedModeLaunchDeck = + HyperTwistCoachDashboardWidgetInternal::IsGeneratedModeLaunchDeck(CachedRunState); + const bool bCanExecuteGeneratedModeLaunch = + bGeneratedModeLaunchDeck + && CachedCoachPanelState.bCanExecuteActiveGeneratedModeLaunchRequest; + const bool bRecognitionAssistedCurrentRun = + CachedRunState.Session.Mode == EHyperTwistTrainingDeliveryMode::RecognitionAssisted; + const bool bRecognitionSessionOpenForCurrentRun = + !bRecognitionAssistedCurrentRun || CachedRecognitionSessionState.bSessionOpen; + const bool bRecognitionServiceReadyForCurrentRun = + !bRecognitionAssistedCurrentRun || CachedRecognitionSessionState.ServiceHealth.bReady; + const FString RecognitionRunBlockedReason = + !CachedRecognitionSessionState.LastError.IsEmpty() + ? CachedRecognitionSessionState.LastError + : (!CachedRecognitionSessionState.ServiceHealth.LastError.IsEmpty() + ? CachedRecognitionSessionState.ServiceHealth.LastError + : TEXT("recognition-session-not-open")); + const bool bHasRunnableCurrentCase = + bHasActiveRun && CachedRunState.CurrentSelection.TrainingCase.IsStructurallyValid(); + const bool bCanContinueCurrentRun = !bHasLiveAttemptTimer + && bHasRunnableCurrentCase + && !bLastStepCompletedRun + && (!bRecognitionAssistedCurrentRun + || bRecognitionSessionOpenForCurrentRun + || bRecognitionServiceReadyForCurrentRun) + && (!bGeneratedModeLaunchDeck || bCanExecuteGeneratedModeLaunch); + const bool bCanLaunchCoachRun = !bHasLiveAttemptTimer + && ( + CachedCoachPanelState.bCanStartQueuedRun + || CachedCoachPanelState.bCanStartCoachFollowUpRun + || CachedCoachPanelState.bCanStartCoachRecommendedRun); + FString TransitionLaunchLabel = CachedCoachPanelState.PrimaryActionLabel; + if (TransitionLaunchLabel.IsEmpty()) + { + TransitionLaunchLabel = TEXT("Launch Coach Run"); + } + + Surface.TransitionLaunchLabel = TransitionLaunchLabel; + Surface.ActiveCaseId = ActiveCaseId; + Surface.ActivePromptLabel = ActivePromptLabel; + Surface.RecognitionBlockedReason = RecognitionRunBlockedReason; + Surface.AttemptCount = CachedRunSummary.AttemptCount; + Surface.bHasLiveAttemptTimer = bHasLiveAttemptTimer; + Surface.bLiveInspectionPhase = bLiveInspectionPhase; + Surface.bLiveSolvePhase = bLiveSolvePhase; + Surface.bHasActiveRun = bHasActiveRun; + Surface.bLastStepCompletedRun = bLastStepCompletedRun; + Surface.bHasNextCaseFromLastStep = bHasNextCaseFromLastStep; + Surface.bGeneratedModeLaunchDeck = bGeneratedModeLaunchDeck; + Surface.bCanExecuteGeneratedModeLaunch = bCanExecuteGeneratedModeLaunch; + Surface.bRecognitionAssistedCurrentRun = bRecognitionAssistedCurrentRun; + Surface.bRecognitionSessionOpenForCurrentRun = bRecognitionSessionOpenForCurrentRun; + Surface.bRecognitionServiceReadyForCurrentRun = bRecognitionServiceReadyForCurrentRun; + Surface.bCanContinueCurrentRun = bCanContinueCurrentRun; + Surface.bCanLaunchCoachRun = bCanLaunchCoachRun; + + const bool bQueueRecoveryMemorySource = + HyperTwistCoachDashboardWidgetInternal::IsQueueRecoveryMemorySource( + CachedCoachPanelState.PreferredGuidanceSourceLabel); + Surface.bClosedLoopIdleDashboard = + IsCoachLoopActionabilityClosed() + && ( + bQueueRecoveryMemorySource + || !CachedCoachPanelState.PreferredGuidanceSourceLabel.IsEmpty() + || DisplayedCoachHandoffRecommendationKind != EHyperTwistTrainingCoachHandoffKind::None); + + Surface.StatusLine = !DisplayedTransitionStatusLine.IsEmpty() + ? DisplayedTransitionStatusLine + : FString::Printf(TEXT("Transition: no run is active. Use the next-step action to %s."), *TransitionLaunchLabel); + if (Surface.bClosedLoopIdleDashboard) + { + Surface.DetailLine = + TEXT("Transition detail: the coach-to-review loop is closed and idle, so no runnable continuation or coach-directed launch remains active."); + } + else if (Surface.bLiveInspectionPhase) + { + Surface.DetailLine = FString::Printf( + TEXT("Transition detail: inspection is live on %s."), + *Surface.ActivePromptLabel); + } + else if (Surface.bLiveSolvePhase) + { + Surface.DetailLine = FString::Printf( + TEXT("Transition detail: solve is live on %s."), + *Surface.ActivePromptLabel); + } + else if (Surface.bLastStepCompletedRun) + { + Surface.DetailLine = FString::Printf( + TEXT("Transition detail: last step completed the run after case %s | coach launch available: %s"), + LastStepResult.SessionSummary.LastCaseId.IsEmpty() + ? TEXT("n/a") + : *LastStepResult.SessionSummary.LastCaseId, + Surface.bCanLaunchCoachRun ? TEXT("yes") : TEXT("no")); + } + else if (Surface.bGeneratedModeLaunchDeck) + { + Surface.DetailLine = FString::Printf( + TEXT("Transition detail: generated-mode launch deck is active on %s | execute ready: %s"), + *Surface.ActivePromptLabel, + Surface.bCanExecuteGeneratedModeLaunch ? TEXT("yes") : TEXT("no")); + } + else if (Surface.bRecognitionAssistedCurrentRun) + { + Surface.DetailLine = FString::Printf( + TEXT("Transition detail: recognition-assisted run on %s | session open: %s | service ready: %s | blocked reason: %s"), + *Surface.ActivePromptLabel, + Surface.bRecognitionSessionOpenForCurrentRun ? TEXT("yes") : TEXT("no"), + Surface.bRecognitionServiceReadyForCurrentRun ? TEXT("yes") : TEXT("no"), + *Surface.RecognitionBlockedReason); + } + else if (Surface.bCanContinueCurrentRun) + { + Surface.DetailLine = FString::Printf( + TEXT("Transition detail: runnable case %s | attempts %d | next-case-from-last-step: %s"), + *Surface.ActivePromptLabel, + Surface.AttemptCount, + Surface.bHasNextCaseFromLastStep ? TEXT("yes") : TEXT("no")); + } + else if (Surface.bHasActiveRun) + { + Surface.DetailLine = + TEXT("Transition detail: an active run exists but no runnable current case remains."); + } + else + { + Surface.DetailLine = FString::Printf( + TEXT("Transition detail: no run is active | coach launch available: %s | launch label: %s"), + Surface.bCanLaunchCoachRun ? TEXT("yes") : TEXT("no"), + *Surface.TransitionLaunchLabel); + } + return Surface; +} + +FHyperTwistTrainingCoachDashboardRunActionLabelsInspectSurface +UHyperTwistCoachDashboardWidget::GetDisplayedRunActionLabelsInspectSurface() const +{ + FHyperTwistTrainingCoachDashboardRunActionLabelsInspectSurface Surface; + Surface.Headline = TEXT("Run action labels"); + + 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"); + } + }; + + FString TransitionLaunchLabel = CachedCoachPanelState.PrimaryActionLabel; + if (TransitionLaunchLabel.IsEmpty()) + { + TransitionLaunchLabel = TEXT("Launch Coach Run"); + } + + Surface.ContinueRunLabel = + DisplayedContinueRunLabel.IsEmpty() ? TransitionLaunchLabel : DisplayedContinueRunLabel; + Surface.QueuedRunLabel = + DisplayedQueuedRunLabel.IsEmpty() ? TEXT("Start Queue") : DisplayedQueuedRunLabel; + Surface.FollowUpRunLabel = + DisplayedFollowUpRunLabel.IsEmpty() ? TEXT("Start Follow-Up") : DisplayedFollowUpRunLabel; + Surface.RecommendedRunLabel = + DisplayedRecommendedRunLabel.IsEmpty() ? TEXT("Start Recommended") : DisplayedRecommendedRunLabel; + Surface.RecommendedHandoffKindLabel = + DescribeDisplayedHandoffKind(DisplayedCoachHandoffRecommendationKind); + Surface.TransitionLaunchLabel = TransitionLaunchLabel; + Surface.bContinueUsesRecognitionPrimeLabel = + Surface.ContinueRunLabel == TEXT("Prime Recognition") + || Surface.ContinueRunLabel == TEXT("Recognition Blocked"); + Surface.bContinueUsesGeneratedModeExecuteLabel = + Surface.ContinueRunLabel == TEXT("Execute Generated Run"); + Surface.bContinueUsesGeneratedModeResolveLabel = + Surface.ContinueRunLabel == TEXT("Resolve Selectors"); + Surface.bContinueUsesAttemptActiveLabel = + Surface.ContinueRunLabel == TEXT("Start Solve") + || Surface.ContinueRunLabel == TEXT("Attempt Active"); + Surface.bQueueActionRecommended = + Surface.QueuedRunLabel.StartsWith(TEXT("Recommended: ")); + Surface.bFollowUpActionRecommended = + Surface.FollowUpRunLabel.StartsWith(TEXT("Recommended: ")); + Surface.bRecommendedActionRecommended = + Surface.RecommendedRunLabel.StartsWith(TEXT("Recommended: ")); + + const bool bQueueRecoveryMemorySource = + HyperTwistCoachDashboardWidgetInternal::IsQueueRecoveryMemorySource( + CachedCoachPanelState.PreferredGuidanceSourceLabel); + Surface.bClosedLoopIdleDashboard = + IsCoachLoopActionabilityClosed() + && ( + bQueueRecoveryMemorySource + || !CachedCoachPanelState.PreferredGuidanceSourceLabel.IsEmpty() + || DisplayedCoachHandoffRecommendationKind != EHyperTwistTrainingCoachHandoffKind::None); + + Surface.StatusLine = FString::Printf( + TEXT("Continue: %s | Queue: %s | Follow-up: %s | Recommended: %s"), + *Surface.ContinueRunLabel, + *Surface.QueuedRunLabel, + *Surface.FollowUpRunLabel, + *Surface.RecommendedRunLabel); + Surface.DetailLine = FString::Printf( + TEXT("Action-label detail: recommended handoff branch %s | transition launch label %s"), + *Surface.RecommendedHandoffKindLabel, + *Surface.TransitionLaunchLabel); + Surface.DetailLine += Surface.bContinueUsesGeneratedModeExecuteLabel + ? TEXT(" | continue is currently an execute-generated-run action") + : TEXT(""); + Surface.DetailLine += Surface.bContinueUsesGeneratedModeResolveLabel + ? TEXT(" | continue is currently a selector-resolution action") + : TEXT(""); + Surface.DetailLine += Surface.bContinueUsesRecognitionPrimeLabel + ? TEXT(" | continue is currently a recognition priming gate") + : TEXT(""); + Surface.DetailLine += Surface.bContinueUsesAttemptActiveLabel + ? TEXT(" | continue is currently carrying the live-attempt control label") + : TEXT(""); + if (Surface.bClosedLoopIdleDashboard) + { + Surface.DetailLine = + TEXT("Action-label detail: closed-loop idle keeps the labels inspectable, but no runnable continuation or coach-directed launch remains active."); + } + return Surface; +} + bool UHyperTwistCoachDashboardWidget::SelectPreviousMethodDrillFollowUpHistoryEntry() { if (MethodDrillFollowUpHistoryEntries.Num() <= 0) @@ -20733,6 +21073,12 @@ void UHyperTwistCoachDashboardWidget::UpdateDashboardPresentation() DisplayedFollowUpDeckPreviewLine = FollowUpDeckPreviewLine; DisplayedCoachHandoffRecommendationStatusLine = CoachHandoffRecommendationStatusLine; DisplayedCoachHandoffRecommendationDetailLine = CoachHandoffRecommendationDetailLine; + DisplayedCoachHandoffStatusLine = CoachHandoffStatusLine; + DisplayedTransitionStatusLine = TransitionStatusLine; + DisplayedContinueRunLabel = ContinueRunLabel; + DisplayedQueuedRunLabel = QueuedRunLabel; + DisplayedFollowUpRunLabel = FollowUpRunLabel; + DisplayedRecommendedRunLabel = RecommendedRunLabel; RecordCoachHandoffHistorySnapshot( HyperTwistCoachDashboardWidgetInternal::DescribeHandoffRecommendationKind( HandoffRecommendationKind), diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistCoachDashboardWidget.h b/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistCoachDashboardWidget.h index db7d7f5..b9c1c0d 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistCoachDashboardWidget.h +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistCoachDashboardWidget.h @@ -803,6 +803,18 @@ public: FHyperTwistTrainingCoachDashboardHandoffRecommendationInspectSurface GetDisplayedCoachHandoffRecommendationInspectSurface() const; + UFUNCTION(BlueprintPure, Category = "HyperTwist|Training|Coach|Dashboard") + FHyperTwistTrainingCoachDashboardHandoffStatusInspectSurface + GetDisplayedCoachHandoffStatusInspectSurface() const; + + UFUNCTION(BlueprintPure, Category = "HyperTwist|Training|Coach|Dashboard") + FHyperTwistTrainingCoachDashboardTransitionStatusInspectSurface + GetDisplayedTransitionStatusInspectSurface() const; + + UFUNCTION(BlueprintPure, Category = "HyperTwist|Training|Coach|Dashboard") + FHyperTwistTrainingCoachDashboardRunActionLabelsInspectSurface + GetDisplayedRunActionLabelsInspectSurface() const; + UFUNCTION(BlueprintCallable, Category = "HyperTwist|Training|Coach|Dashboard") bool SelectPreviousMethodDrillFollowUpHistoryEntry(); @@ -1842,8 +1854,14 @@ private: FString DisplayedCoachHandoffMethodDrillRecoveryMemoryStartActionSourceLabel; FString DisplayedCoachHandoffRecommendationStatusLine; FString DisplayedCoachHandoffRecommendationDetailLine; + FString DisplayedCoachHandoffStatusLine; + FString DisplayedTransitionStatusLine; FString DisplayedRecommendedDeckPreviewLine; FString DisplayedFollowUpDeckPreviewLine; + FString DisplayedContinueRunLabel; + FString DisplayedQueuedRunLabel; + FString DisplayedFollowUpRunLabel; + FString DisplayedRecommendedRunLabel; 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 e896663..6c56d58 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingTypes.h +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingTypes.h @@ -10821,6 +10821,223 @@ struct FHyperTwistTrainingCoachDashboardHandoffRecommendationInspectSurface } }; +USTRUCT(BlueprintType) +struct FHyperTwistTrainingCoachDashboardHandoffStatusInspectSurface +{ + 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 QueueStatusLabel; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString FollowUpStatusLabel; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString RecommendedStatusLabel; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString QueueSourceLabel; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString FollowUpBriefHeadline; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString RecommendedBriefHeadline; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + int32 FollowUpFocusCaseCount = 0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + int32 RecommendedFocusCaseCount = 0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bHasLiveAttemptTimer = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bHasActiveRun = 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 bClosedLoopIdleDashboard = false; + + bool IsStructurallyValid() const + { + return !StatusLine.IsEmpty() + || !DetailLine.IsEmpty() + || bQueueReady + || bFollowUpReady + || bRecommendedReady + || bHasLiveAttemptTimer + || bHasActiveRun; + } +}; + +USTRUCT(BlueprintType) +struct FHyperTwistTrainingCoachDashboardTransitionStatusInspectSurface +{ + 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 TransitionLaunchLabel; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString ActiveCaseId; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString ActivePromptLabel; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString RecognitionBlockedReason; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + int32 AttemptCount = 0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bHasLiveAttemptTimer = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bLiveInspectionPhase = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bLiveSolvePhase = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bHasActiveRun = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bLastStepCompletedRun = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bHasNextCaseFromLastStep = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bGeneratedModeLaunchDeck = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bCanExecuteGeneratedModeLaunch = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bRecognitionAssistedCurrentRun = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bRecognitionSessionOpenForCurrentRun = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bRecognitionServiceReadyForCurrentRun = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bCanContinueCurrentRun = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bCanLaunchCoachRun = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bClosedLoopIdleDashboard = false; + + bool IsStructurallyValid() const + { + return !StatusLine.IsEmpty() + || !DetailLine.IsEmpty() + || bHasLiveAttemptTimer + || bHasActiveRun + || bCanContinueCurrentRun + || bCanLaunchCoachRun; + } +}; + +USTRUCT(BlueprintType) +struct FHyperTwistTrainingCoachDashboardRunActionLabelsInspectSurface +{ + 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 ContinueRunLabel; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString QueuedRunLabel; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString FollowUpRunLabel; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString RecommendedRunLabel; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString RecommendedHandoffKindLabel; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString TransitionLaunchLabel; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bContinueUsesRecognitionPrimeLabel = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bContinueUsesGeneratedModeExecuteLabel = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bContinueUsesGeneratedModeResolveLabel = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bContinueUsesAttemptActiveLabel = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bQueueActionRecommended = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bFollowUpActionRecommended = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bRecommendedActionRecommended = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bClosedLoopIdleDashboard = false; + + bool IsStructurallyValid() const + { + return !StatusLine.IsEmpty() + || !DetailLine.IsEmpty() + || !ContinueRunLabel.IsEmpty() + || !QueuedRunLabel.IsEmpty() + || !FollowUpRunLabel.IsEmpty() + || !RecommendedRunLabel.IsEmpty(); + } +}; + USTRUCT(BlueprintType) struct FHyperTwistTrainingCoachGeneratedModeLaunchExecutionContextSurface { diff --git a/docs/HYPERTWIST_ROADMAP_OVERHAUL_EXPANSION_GUIDE.md b/docs/HYPERTWIST_ROADMAP_OVERHAUL_EXPANSION_GUIDE.md index 37cdd0e..3e93e7d 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 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 latest landed bounded `Phase 4` packet stays on that same live `UHyperTwistCoachDashboardWidget` guidance-consumer lane and adds displayed coach handoff status, displayed transition status, and displayed continuation / queued / follow-up / recommended run action-label inspect surfaces over already-derived live dashboard transition and action-label state, so gameplay/Blueprint callers can inspect the current transition posture, coach handoff availability line, and surfaced launch/control labels without scraping dashboard text blocks or reopening transition/handoff derivation + - the next clean move is to stay on that same live dashboard guidance-consumer lane and land the displayed active-run status, active-attempt/timer status, and current-case / current-deck status 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_HANDOFF_STATUS_TRANSITION_STATUS_RUN_ACTION_LABEL_INSPECT_SURFACE_PACKET_2026-05-09.md b/docs/arch/HYPERTWIST_PHASE4_DASHBOARD_LIVE_HANDOFF_STATUS_TRANSITION_STATUS_RUN_ACTION_LABEL_INSPECT_SURFACE_PACKET_2026-05-09.md new file mode 100644 index 0000000..5e5dcf4 --- /dev/null +++ b/docs/arch/HYPERTWIST_PHASE4_DASHBOARD_LIVE_HANDOFF_STATUS_TRANSITION_STATUS_RUN_ACTION_LABEL_INSPECT_SURFACE_PACKET_2026-05-09.md @@ -0,0 +1,55 @@ +# HyperTwist Phase 4 dashboard live handoff status, transition status, and run action-label 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 live handoff-status, transition-status, and surfaced run action-label state directly to gameplay and Blueprint callers. + +## Why this packet exists + +The dashboard already derived: + +- the displayed coach handoff availability/status line +- the displayed transition-status line +- the displayed continue / queue / follow-up / recommended run button labels + +But that live state still only surfaced through dashboard text blocks and button labels. Callers still had to scrape widget presentation or reopen transition / handoff derivation to inspect what the dashboard was currently showing. + +## What changed + +- added `FHyperTwistTrainingCoachDashboardHandoffStatusInspectSurface` +- added `FHyperTwistTrainingCoachDashboardTransitionStatusInspectSurface` +- added `FHyperTwistTrainingCoachDashboardRunActionLabelsInspectSurface` +- added `GetDisplayedCoachHandoffStatusInspectSurface()` +- added `GetDisplayedTransitionStatusInspectSurface()` +- added `GetDisplayedRunActionLabelsInspectSurface()` +- retained the exact displayed handoff-status line, transition-status line, and surfaced run action labels 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 coach handoff status line and current queue / follow-up / recommended readiness posture +- the displayed transition status line and current live transition posture +- the surfaced continue / queue / follow-up / recommended run action labels, including which branch is currently marked as recommended + +without scraping dashboard text blocks or button labels or reopening transition / 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 active-run status, active-attempt/timer status, and current-case / current-deck status inspect surface group.