From fdcfe1aa986ac2f6359720a77171241df5cbd031 Mon Sep 17 00:00:00 2001 From: axiomlogicnexus Date: Fri, 8 May 2026 23:01:56 +0200 Subject: [PATCH] Expose gameplay selector phase-id fallback roster inspect surface --- .../HyperTwistTrainingCoachLibrary.cpp | 242 ++++++++++++++++++ .../HyperTwistTrainingPanelWidget.cpp | 19 ++ .../HyperTwistTrainingSessionActor.cpp | 19 ++ .../HyperTwistTrainingCoachLibrary.h | 10 + .../HyperTwistTrainingPanelWidget.h | 6 + .../HyperTwistTrainingSessionActor.h | 6 + .../HyperTwistTrainingTypes.h | 145 +++++++++++ ...RTWIST_ROADMAP_OVERHAUL_EXPANSION_GUIDE.md | 4 +- ...OSTER_INSPECT_SURFACE_PACKET_2026-05-08.md | 33 +++ 9 files changed, 482 insertions(+), 2 deletions(-) create mode 100644 docs/arch/HYPERTWIST_PHASE4_GAMEPLAY_GENERATED_MODE_SELECTOR_OPTION_OWNED_EXECUTION_SPLIT_PHASE_CURRENT_SELECTION_PHASE_ID_FALLBACK_ROSTER_INSPECT_SURFACE_PACKET_2026-05-08.md diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingCoachLibrary.cpp b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingCoachLibrary.cpp index a170f71..d263710 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingCoachLibrary.cpp +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingCoachLibrary.cpp @@ -11515,6 +11515,248 @@ UHyperTwistTrainingCoachLibrary:: return Surface; } +FHyperTwistTrainingCoachGeneratedModeSelectorOptionOwnedExecutionSplitPhaseCurrentSelectionPhaseIdFallbackRosterInspectSurface +UHyperTwistTrainingCoachLibrary:: + DeriveCoachGeneratedModeSelectorOptionOwnedExecutionSplitPhaseCurrentSelectionPhaseIdFallbackRosterInspectSurface( + const FHyperTwistTrainingRunState& RunState, + const FString& SelectorId, + const FHyperTwistTrainingCoachGeneratedModeSelectorOptionMenuSurface& OptionMenuSurface, + const FHyperTwistTrainingCoachGeneratedModeSelectorOptionLaunchRequestPreviewSurface& + OptionLaunchRequestPreviewSurface + ) +{ + const FHyperTwistTrainingCoachGeneratedModeSelectorOptionOwnedExecutionSplitPhaseCurrentSelectionSurvivorPhaseRosterInspectSurface + SurvivorRosterSurface = + DeriveCoachGeneratedModeSelectorOptionOwnedExecutionSplitPhaseCurrentSelectionSurvivorPhaseRosterInspectSurface( + RunState, + SelectorId, + OptionMenuSurface, + OptionLaunchRequestPreviewSurface + ); + + FHyperTwistTrainingCoachGeneratedModeSelectorOptionOwnedExecutionSplitPhaseCurrentSelectionPhaseIdFallbackRosterInspectSurface + Surface; + Surface.SelectorId = SurvivorRosterSurface.SelectorId; + Surface.SelectorLabel = SurvivorRosterSurface.SelectorLabel; + Surface.BindingLabel = SurvivorRosterSurface.BindingLabel; + Surface.Headline = + TEXT("Generated Mode Selector Option Owned-Execution Split-Phase Current-Selection Phase-Id Fallback Roster Inspect"); + Surface.bHasSelector = SurvivorRosterSurface.bHasSelector; + Surface.bHasSelection = SurvivorRosterSurface.bHasSelection; + Surface.bSelectionMatchesAvailableOption = + SurvivorRosterSurface.bSelectionMatchesAvailableOption; + Surface.CurrentSelectionSelectionLine = + SurvivorRosterSurface.CurrentSelectionSelectionLine; + Surface.CurrentSelectionOptionId = + SurvivorRosterSurface.CurrentSelectionOptionId; + Surface.CurrentSelectionOptionLabel = + SurvivorRosterSurface.CurrentSelectionOptionLabel; + Surface.CurrentSelectionOptionValue = + SurvivorRosterSurface.CurrentSelectionOptionValue; + Surface.LaunchRequestPreviewKind = + SurvivorRosterSurface.LaunchRequestPreviewKind; + Surface.bStartsOwnedCaseImmediately = + SurvivorRosterSurface.bStartsOwnedCaseImmediately; + Surface.bHasExactCurrentSelectionPhaseIdFallbackRoster = + SurvivorRosterSurface.bHasExactCurrentSelectionSurvivorPhaseRoster; + Surface.bRetainsActiveDeckSplitPhaseFusion = + SurvivorRosterSurface.bRetainsActiveDeckSplitPhaseFusion; + Surface.bPreviewBlocked = SurvivorRosterSurface.bPreviewBlocked; + Surface.bCanPreview = SurvivorRosterSurface.bCanPreview; + + if (!SelectorId.IsEmpty()) + { + Surface.Headline = OptionMenuSurface.SelectorLabel.IsEmpty() + ? FString::Printf( + TEXT("Generated Mode Selector Option Owned-Execution Split-Phase Current-Selection Phase-Id Fallback Roster Inspect: %s"), + *SelectorId + ) + : FString::Printf( + TEXT("Generated Mode Selector Option Owned-Execution Split-Phase Current-Selection Phase-Id Fallback Roster Inspect: %s"), + *OptionMenuSurface.SelectorLabel + ); + } + + for (const FHyperTwistTrainingCoachGeneratedModeSelectorOptionOwnedExecutionSplitPhaseCurrentSelectionSurvivorPhaseRosterInspectPhaseSurface& + SurvivorPhase : SurvivorRosterSurface.SurvivorPhases) + { + if (!SurvivorPhase.bLabelFallsBackToPhaseId) + { + continue; + } + + FHyperTwistTrainingCoachGeneratedModeSelectorOptionOwnedExecutionSplitPhaseCurrentSelectionPhaseIdFallbackRosterInspectPhaseSurface + PhaseSurface; + PhaseSurface.PhaseId = SurvivorPhase.PhaseId; + PhaseSurface.SourceLabel = SurvivorPhase.SourceLabel; + PhaseSurface.NormalizedLabel = SurvivorPhase.NormalizedLabel; + PhaseSurface.EvaluationOrder = SurvivorPhase.EvaluationOrder; + PhaseSurface.NormalizedOrder = SurvivorPhase.NormalizedOrder; + PhaseSurface.OrderLine = SurvivorPhase.OrderLine; + PhaseSurface.LabelLine = SurvivorPhase.LabelLine; + PhaseSurface.LabelProvenanceLine = SurvivorPhase.LabelProvenanceLine; + PhaseSurface.PhaseIdFallbackRosterLine = FString::Printf( + TEXT("Current-selection phase-id fallback roster on execute: evaluation #%d survives as normalized phase #%d with phase-id fallback label %s (%s)."), + SurvivorPhase.EvaluationOrder, + SurvivorPhase.NormalizedOrder, + *SurvivorPhase.NormalizedLabel, + *SurvivorPhase.PhaseId + ); + PhaseSurface.DetailLine = FString::Printf( + TEXT("%s | %s | %s"), + *PhaseSurface.OrderLine, + *PhaseSurface.LabelProvenanceLine, + *PhaseSurface.PhaseIdFallbackRosterLine + ); + PhaseSurface.bCanPreview = SurvivorPhase.bCanPreview; + Surface.FallbackPhases.Add(MoveTemp(PhaseSurface)); + } + + Surface.FallbackPhaseCount = Surface.FallbackPhases.Num(); + Surface.bHasCurrentSelectionPhaseIdFallbackRoster = + Surface.FallbackPhaseCount > 0; + + Surface.FallbackPhaseCountLine = Surface.bHasCurrentSelectionPhaseIdFallbackRoster + ? FString::Printf( + TEXT("Current-selection phase-id fallback phases on execute: %d retained phase%s"), + Surface.FallbackPhaseCount, + Surface.FallbackPhaseCount == 1 ? TEXT("") : TEXT("s") + ) + : (SurvivorRosterSurface.bHasCurrentSelectionSurvivorPhaseRoster + && !SurvivorRosterSurface.bPreviewBlocked + ? TEXT("Current-selection phase-id fallback phases on execute: none") + : TEXT("Current-selection phase-id fallback phases on execute: unavailable")); + + if (Surface.FallbackPhases.Num() > 0) + { + TArray OrderedPhaseLabels; + OrderedPhaseLabels.Reserve(Surface.FallbackPhases.Num()); + TArray NormalizedLabels; + NormalizedLabels.Reserve(Surface.FallbackPhases.Num()); + for (const FHyperTwistTrainingCoachGeneratedModeSelectorOptionOwnedExecutionSplitPhaseCurrentSelectionPhaseIdFallbackRosterInspectPhaseSurface& + PhaseSurface : Surface.FallbackPhases) + { + OrderedPhaseLabels.Add( + FString::Printf(TEXT("#%d %s"), PhaseSurface.NormalizedOrder, *PhaseSurface.NormalizedLabel) + ); + NormalizedLabels.Add( + FString::Printf(TEXT("%s (%s)"), *PhaseSurface.NormalizedLabel, *PhaseSurface.PhaseId) + ); + } + + Surface.OrderSummaryLine = FString::Printf( + TEXT("Current-selection phase-id fallback order on execute: %s"), + *FString::Join(OrderedPhaseLabels, TEXT(" | ")) + ); + Surface.LabelSummaryLine = FString::Printf( + TEXT("Current-selection phase-id fallback labels on execute: %s"), + *FString::Join(NormalizedLabels, TEXT(", ")) + ); + Surface.CurrentSelectionPhaseIdFallbackRosterLine = Surface.FallbackPhaseCountLine; + } + else if (OptionMenuSurface.bHasUnresolvedSelectionDetail) + { + Surface.OrderSummaryLine = + TEXT("Current-selection phase-id fallback order on execute: unavailable until the cached selection resolves to an imported option."); + Surface.LabelSummaryLine = + TEXT("Current-selection phase-id fallback labels on execute: unavailable until the cached selection resolves to an imported option."); + Surface.CurrentSelectionPhaseIdFallbackRosterLine = + OptionMenuSurface.UnresolvedSelectionLine; + } + else if (SurvivorRosterSurface.bPreviewBlocked) + { + Surface.OrderSummaryLine = + TEXT("Current-selection phase-id fallback order on execute: blocked until execute-time survivor-phase inspection is available."); + Surface.LabelSummaryLine = + TEXT("Current-selection phase-id fallback labels on execute: blocked until execute-time survivor-phase inspection is available."); + Surface.CurrentSelectionPhaseIdFallbackRosterLine = + SurvivorRosterSurface.CurrentSelectionSurvivorRosterLine; + } + else if (SurvivorRosterSurface.bHasCurrentSelectionSurvivorPhaseRoster) + { + Surface.OrderSummaryLine = + TEXT("Current-selection phase-id fallback order on execute: none"); + Surface.LabelSummaryLine = + TEXT("Current-selection phase-id fallback labels on execute: none"); + Surface.CurrentSelectionPhaseIdFallbackRosterLine = + TEXT("Current-selection phase-id fallback roster on execute: none"); + } + else if (OptionMenuSurface.bHasSelection) + { + Surface.OrderSummaryLine = + TEXT("Current-selection phase-id fallback order on execute: unavailable"); + Surface.LabelSummaryLine = + TEXT("Current-selection phase-id fallback labels on execute: unavailable"); + Surface.CurrentSelectionPhaseIdFallbackRosterLine = + TEXT("Current selection has no additional owned-execution split-phase phase-id fallback roster preview."); + } + else if (OptionMenuSurface.bHasSelector) + { + Surface.OrderSummaryLine = + TEXT("Current-selection phase-id fallback order on execute: unresolved"); + Surface.LabelSummaryLine = + TEXT("Current-selection phase-id fallback labels on execute: unresolved"); + Surface.CurrentSelectionPhaseIdFallbackRosterLine = + TEXT("Current selection: unresolved"); + } + else + { + Surface.OrderSummaryLine = + TEXT("Current-selection phase-id fallback order on execute: n/a"); + Surface.LabelSummaryLine = + TEXT("Current-selection phase-id fallback labels on execute: n/a"); + Surface.CurrentSelectionPhaseIdFallbackRosterLine = + TEXT("Current selection: n/a"); + } + + if (Surface.bHasCurrentSelectionPhaseIdFallbackRoster) + { + Surface.StatusLine = FString::Printf( + TEXT("Generated-mode selector option owned-execution split-phase current-selection phase-id fallback roster inspect exposes %d fallback phase%s for the active selection."), + Surface.FallbackPhaseCount, + Surface.FallbackPhaseCount == 1 ? TEXT("") : TEXT("s") + ); + } + else if (OptionMenuSurface.bHasSelector && OptionMenuSurface.bHasUnresolvedSelectionDetail) + { + Surface.StatusLine = + TEXT("Generated-mode selector option owned-execution split-phase current-selection phase-id fallback roster inspect is active, but the cached selection no longer matches an imported option."); + } + else if (SurvivorRosterSurface.bPreviewBlocked) + { + Surface.StatusLine = + TEXT("Generated-mode selector option owned-execution split-phase current-selection phase-id fallback roster inspect resolves the active selection, but execute-time fallback-phase inspection remains blocked."); + } + else if (SurvivorRosterSurface.bHasCurrentSelectionSurvivorPhaseRoster) + { + Surface.StatusLine = + TEXT("Generated-mode selector option owned-execution split-phase current-selection phase-id fallback roster inspect resolves the active selection, and no phase-id fallback phases are currently retained."); + } + else if (OptionMenuSurface.bHasSelector && OptionMenuSurface.bHasSelection) + { + Surface.StatusLine = + TEXT("Generated-mode selector option owned-execution split-phase current-selection phase-id fallback roster inspect is active, but the current selection does not expose fallback-phase detail."); + } + else if (OptionMenuSurface.bHasSelector) + { + Surface.StatusLine = + TEXT("Generated-mode selector option owned-execution split-phase current-selection phase-id fallback roster inspect is active, but this selector is currently unresolved."); + } + else + { + Surface.StatusLine = SurvivorRosterSurface.StatusLine; + } + + Surface.DetailLine = FString::Printf( + TEXT("%s | %s | %s"), + *Surface.CurrentSelectionSelectionLine, + *Surface.CurrentSelectionPhaseIdFallbackRosterLine, + *Surface.OrderSummaryLine + ); + + return Surface; +} + FHyperTwistTrainingCoachQueueControlSurface UHyperTwistTrainingCoachLibrary::DeriveCoachQueueControlSurface( const FHyperTwistTrainingCoachSessionQueueState& QueueState, diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingPanelWidget.cpp b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingPanelWidget.cpp index 97469d5..7120315 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingPanelWidget.cpp +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingPanelWidget.cpp @@ -1214,6 +1214,25 @@ UHyperTwistTrainingPanelWidget::GetDisplayedCoachGeneratedModeSelectorOptionOwne ); } +FHyperTwistTrainingCoachGeneratedModeSelectorOptionOwnedExecutionSplitPhaseCurrentSelectionPhaseIdFallbackRosterInspectSurface +UHyperTwistTrainingPanelWidget::GetDisplayedCoachGeneratedModeSelectorOptionOwnedExecutionSplitPhaseCurrentSelectionPhaseIdFallbackRosterInspectSurface( + const FString& SelectorId +) const +{ + const FHyperTwistTrainingCoachGeneratedModeSelectorOptionMenuSurface OptionMenuSurface = + GetDisplayedCoachGeneratedModeSelectorOptionMenuSurface(SelectorId); + const FHyperTwistTrainingCoachGeneratedModeSelectorOptionLaunchRequestPreviewSurface + OptionLaunchRequestPreviewSurface = + GetDisplayedCoachGeneratedModeSelectorOptionLaunchRequestPreviewSurface(SelectorId); + return UHyperTwistTrainingCoachLibrary:: + DeriveCoachGeneratedModeSelectorOptionOwnedExecutionSplitPhaseCurrentSelectionPhaseIdFallbackRosterInspectSurface( + CachedRunState, + SelectorId, + OptionMenuSurface, + OptionLaunchRequestPreviewSurface + ); +} + FHyperTwistTrainingRunState UHyperTwistTrainingPanelWidget::StartCoachRecommendedRun( const int32 MaxCases, const FString& StartActionSourceLabel diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingSessionActor.cpp b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingSessionActor.cpp index cd9b43b..f03d903 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingSessionActor.cpp +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingSessionActor.cpp @@ -1195,6 +1195,25 @@ AHyperTwistTrainingSessionActor::GetDisplayedCoachGeneratedModeSelectorOptionOwn ); } +FHyperTwistTrainingCoachGeneratedModeSelectorOptionOwnedExecutionSplitPhaseCurrentSelectionPhaseIdFallbackRosterInspectSurface +AHyperTwistTrainingSessionActor::GetDisplayedCoachGeneratedModeSelectorOptionOwnedExecutionSplitPhaseCurrentSelectionPhaseIdFallbackRosterInspectSurface( + const FString& SelectorId +) const +{ + const FHyperTwistTrainingCoachGeneratedModeSelectorOptionMenuSurface OptionMenuSurface = + GetDisplayedCoachGeneratedModeSelectorOptionMenuSurface(SelectorId); + const FHyperTwistTrainingCoachGeneratedModeSelectorOptionLaunchRequestPreviewSurface + OptionLaunchRequestPreviewSurface = + GetDisplayedCoachGeneratedModeSelectorOptionLaunchRequestPreviewSurface(SelectorId); + return UHyperTwistTrainingCoachLibrary:: + DeriveCoachGeneratedModeSelectorOptionOwnedExecutionSplitPhaseCurrentSelectionPhaseIdFallbackRosterInspectSurface( + CachedRunState, + SelectorId, + OptionMenuSurface, + OptionLaunchRequestPreviewSurface + ); +} + FHyperTwistTrainingRunState AHyperTwistTrainingSessionActor::StartCoachRecommendedTrainingRun( const int32 MaxCases ) diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingCoachLibrary.h b/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingCoachLibrary.h index 485bab9..958e7ef 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingCoachLibrary.h +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingCoachLibrary.h @@ -957,4 +957,14 @@ public: const FHyperTwistTrainingCoachGeneratedModeSelectorOptionLaunchRequestPreviewSurface& OptionLaunchRequestPreviewSurface ); + + UFUNCTION(BlueprintPure, Category = "HyperTwist|Training|Coach") + static FHyperTwistTrainingCoachGeneratedModeSelectorOptionOwnedExecutionSplitPhaseCurrentSelectionPhaseIdFallbackRosterInspectSurface + DeriveCoachGeneratedModeSelectorOptionOwnedExecutionSplitPhaseCurrentSelectionPhaseIdFallbackRosterInspectSurface( + const FHyperTwistTrainingRunState& RunState, + const FString& SelectorId, + const FHyperTwistTrainingCoachGeneratedModeSelectorOptionMenuSurface& OptionMenuSurface, + const FHyperTwistTrainingCoachGeneratedModeSelectorOptionLaunchRequestPreviewSurface& + OptionLaunchRequestPreviewSurface + ); }; diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingPanelWidget.h b/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingPanelWidget.h index 43b3101..a0f47e1 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingPanelWidget.h +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingPanelWidget.h @@ -404,6 +404,12 @@ public: const FString& SelectorId ) const; + UFUNCTION(BlueprintPure, Category = "HyperTwist|Training|Coach") + FHyperTwistTrainingCoachGeneratedModeSelectorOptionOwnedExecutionSplitPhaseCurrentSelectionPhaseIdFallbackRosterInspectSurface + GetDisplayedCoachGeneratedModeSelectorOptionOwnedExecutionSplitPhaseCurrentSelectionPhaseIdFallbackRosterInspectSurface( + const FString& SelectorId + ) const; + UFUNCTION(BlueprintCallable, Category = "HyperTwist|Training|Coach") FHyperTwistTrainingRunState StartCoachRecommendedRun( int32 MaxCases, diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingSessionActor.h b/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingSessionActor.h index 4ba1efd..c229803 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingSessionActor.h +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingSessionActor.h @@ -385,6 +385,12 @@ public: const FString& SelectorId ) const; + UFUNCTION(BlueprintPure, Category = "HyperTwist|Training|Coach") + FHyperTwistTrainingCoachGeneratedModeSelectorOptionOwnedExecutionSplitPhaseCurrentSelectionPhaseIdFallbackRosterInspectSurface + GetDisplayedCoachGeneratedModeSelectorOptionOwnedExecutionSplitPhaseCurrentSelectionPhaseIdFallbackRosterInspectSurface( + const FString& SelectorId + ) const; + UFUNCTION(BlueprintCallable, Category = "HyperTwist|Training|Coach") FHyperTwistTrainingRunState StartCoachRecommendedTrainingRun(int32 MaxCases); diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingTypes.h b/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingTypes.h index d01c865..bdecb10 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingTypes.h +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingTypes.h @@ -13808,3 +13808,148 @@ struct FHyperTwistTrainingCoachGeneratedModeSelectorOptionOwnedExecutionSplitPha || bHasCurrentSelectionDroppedRowAudit; } }; + +USTRUCT(BlueprintType) +struct FHyperTwistTrainingCoachGeneratedModeSelectorOptionOwnedExecutionSplitPhaseCurrentSelectionPhaseIdFallbackRosterInspectPhaseSurface +{ + GENERATED_BODY() + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString PhaseId; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString SourceLabel; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString NormalizedLabel; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + int32 EvaluationOrder = -1; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + int32 NormalizedOrder = -1; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString OrderLine; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString LabelLine; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString LabelProvenanceLine; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString PhaseIdFallbackRosterLine; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString DetailLine; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bCanPreview = false; + + bool IsStructurallyValid() const + { + return !PhaseId.IsEmpty() + || !NormalizedLabel.IsEmpty() + || !OrderLine.IsEmpty() + || !LabelProvenanceLine.IsEmpty() + || bCanPreview; + } +}; + +USTRUCT(BlueprintType) +struct FHyperTwistTrainingCoachGeneratedModeSelectorOptionOwnedExecutionSplitPhaseCurrentSelectionPhaseIdFallbackRosterInspectSurface +{ + GENERATED_BODY() + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString SelectorId; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString SelectorLabel; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString BindingLabel; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString Headline; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString StatusLine; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString CurrentSelectionSelectionLine; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString CurrentSelectionPhaseIdFallbackRosterLine; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString FallbackPhaseCountLine; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString OrderSummaryLine; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString LabelSummaryLine; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString DetailLine; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + TArray + FallbackPhases; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString CurrentSelectionOptionId; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString CurrentSelectionOptionLabel; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString CurrentSelectionOptionValue; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + EHyperTwistTrainingCoachGeneratedModeOptionLaunchRequestPreviewKind LaunchRequestPreviewKind = + EHyperTwistTrainingCoachGeneratedModeOptionLaunchRequestPreviewKind::None; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + int32 FallbackPhaseCount = 0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bHasSelector = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bHasSelection = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bSelectionMatchesAvailableOption = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bHasCurrentSelectionPhaseIdFallbackRoster = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bStartsOwnedCaseImmediately = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bHasExactCurrentSelectionPhaseIdFallbackRoster = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bRetainsActiveDeckSplitPhaseFusion = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bPreviewBlocked = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bCanPreview = false; + + bool IsStructurallyValid() const + { + return !StatusLine.IsEmpty() + || !SelectorId.IsEmpty() + || !CurrentSelectionPhaseIdFallbackRosterLine.IsEmpty() + || FallbackPhases.Num() > 0 + || bHasSelector + || bHasSelection + || bHasCurrentSelectionPhaseIdFallbackRoster; + } +}; diff --git a/docs/HYPERTWIST_ROADMAP_OVERHAUL_EXPANSION_GUIDE.md b/docs/HYPERTWIST_ROADMAP_OVERHAUL_EXPANSION_GUIDE.md index b203d06..a58f8da 100644 --- a/docs/HYPERTWIST_ROADMAP_OVERHAUL_EXPANSION_GUIDE.md +++ b/docs/HYPERTWIST_ROADMAP_OVERHAUL_EXPANSION_GUIDE.md @@ -68,8 +68,8 @@ Current correction call from the source-exposed audit and current execution refr - the imported generated-mode gap is closed; request/config/selection plumbing and the bounded clean-room executor are already landed in first-party code - 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 latest landed bounded `Phase 4` packet stays on that same gameplay-facing consumer lane and adds one thin generated-mode selector option owned execution split-phase current-selection dropped-row audit inspect surface over the already-cached selector/runtime/request state, so gameplay/Blueprint callers can inspect only duplicate-pruned and invalid raw rows for the active selection without scanning the full fused canonical inspect surface themselves - - the current next bounded packet is to expose one thin generated-mode selector option owned execution split-phase current-selection phase-id fallback roster inspect surface on those same two consumer surfaces so gameplay/Blueprint callers can inspect only surviving phases whose visible labels fall back to phase id without filtering the broader survivor-phase roster themselves + - the latest landed bounded `Phase 4` packet stays on that same gameplay-facing consumer lane and adds one thin generated-mode selector option owned execution split-phase current-selection phase-id fallback roster inspect surface over the already-cached selector/runtime/request state, so gameplay/Blueprint callers can inspect only surviving phases whose visible labels fall back to phase id without filtering the broader survivor-phase roster themselves + - the current next bounded packet is to expose one thin generated-mode selector option owned execution split-phase current-selection explicit-label roster inspect surface on those same two consumer surfaces so gameplay/Blueprint callers can inspect only surviving phases whose visible labels come from explicit source labels without filtering the broader survivor-phase roster themselves - 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_GAMEPLAY_GENERATED_MODE_SELECTOR_OPTION_OWNED_EXECUTION_SPLIT_PHASE_CURRENT_SELECTION_PHASE_ID_FALLBACK_ROSTER_INSPECT_SURFACE_PACKET_2026-05-08.md b/docs/arch/HYPERTWIST_PHASE4_GAMEPLAY_GENERATED_MODE_SELECTOR_OPTION_OWNED_EXECUTION_SPLIT_PHASE_CURRENT_SELECTION_PHASE_ID_FALLBACK_ROSTER_INSPECT_SURFACE_PACKET_2026-05-08.md new file mode 100644 index 0000000..2fb72e2 --- /dev/null +++ b/docs/arch/HYPERTWIST_PHASE4_GAMEPLAY_GENERATED_MODE_SELECTOR_OPTION_OWNED_EXECUTION_SPLIT_PHASE_CURRENT_SELECTION_PHASE_ID_FALLBACK_ROSTER_INSPECT_SURFACE_PACKET_2026-05-08.md @@ -0,0 +1,33 @@ +# HyperTwist Phase 4 gameplay generated-mode selector option owned execution split-phase current-selection phase-id fallback roster inspect surface packet + +## Goal + +Close the next thin gameplay-facing consumer seam above the current-selection survivor-phase roster surface by exposing one current-selection phase-id fallback roster inspect surface for a chosen selector id. + +## Why this packet exists + +The current-selection survivor-phase roster surface already exposed the active selection's normalized surviving phases, including both explicit-label survivors and phase-id fallback survivors. Gameplay and Blueprint callers that only need the fallback subset still had to filter the broader survivor-phase roster themselves. + +This packet removes that caller-side filtering step. It exposes one thin current-selection phase-id fallback roster inspect surface that projects only surviving phases whose visible labels fall back to `PhaseId`, while preserving unresolved-selection and blocked-preview posture from the already-landed survivor-phase roster surface. + +## What changed + +- add a first-party owned execution split-phase current-selection phase-id fallback roster inspect surface in shared training types +- derive that surface in the coach library by projecting the already-landed current-selection survivor-phase roster inspect surface instead of reimplementing split-phase normalization logic +- expose the current-selection phase-id fallback roster inspect surface on both gameplay-facing consumers: + - `UHyperTwistTrainingPanelWidget` + - `AHyperTwistTrainingSessionActor` +- preserve exact fallback-phase order, normalized labels, and label-provenance lines for the active selection +- preserve unresolved cached-selection and blocked-preview posture when the active selection cannot yet expose a fallback-phase roster + +## Validation + +- full `Development Editor|Win64` solution build for `C:\HyperTwist\UnrealHyperTwist\UnrealHyperTwist.sln` + +## Result + +Gameplay and Blueprint callers can now inspect only the active selection's surviving phases whose visible labels fall back to `PhaseId` from one dedicated fallback roster surface without filtering the broader survivor-phase roster first. + +## Next bounded slice + +Stay on the same gameplay-facing consumer lane and expose one thin generated-mode selector option owned execution split-phase current-selection explicit-label roster inspect surface so callers can inspect only surviving phases whose visible labels come from explicit source labels without filtering the broader survivor-phase roster themselves.