Expose dashboard handoff, transition, and action-label surfaces

This commit is contained in:
axiomlogicnexus 2026-05-09 19:52:11 +02:00
parent 5ab380abfe
commit 67ec0915cb
5 changed files with 638 additions and 2 deletions

View file

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

View file

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

View file

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

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

View file

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