diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingCoachLibrary.cpp b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingCoachLibrary.cpp index b6a053f..3f8ea58 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingCoachLibrary.cpp +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingCoachLibrary.cpp @@ -6476,6 +6476,411 @@ UHyperTwistTrainingCoachLibrary::DeriveCoachGeneratedModeSelectorOptionOwnedExec return Surface; } +FHyperTwistTrainingCoachGeneratedModeSelectorOptionOwnedExecutionDeckTitleSelectionPolicyPreviewSurface +UHyperTwistTrainingCoachLibrary:: + DeriveCoachGeneratedModeSelectorOptionOwnedExecutionDeckTitleSelectionPolicyPreviewSurface( + const FHyperTwistTrainingRunState& RunState, + const FString& SelectorId, + const FHyperTwistTrainingCoachGeneratedModeSelectorOptionMenuSurface& OptionMenuSurface, + const FHyperTwistTrainingCoachGeneratedModeSelectorOptionLaunchRequestPreviewSurface& + OptionLaunchRequestPreviewSurface + ) +{ + FHyperTwistTrainingCoachGeneratedModeSelectorOptionOwnedExecutionDeckTitleSelectionPolicyPreviewSurface + Surface; + Surface.SelectorId = SelectorId; + Surface.SelectorLabel = OptionMenuSurface.SelectorLabel; + Surface.BindingLabel = OptionMenuSurface.BindingLabel; + Surface.Headline = + TEXT("Generated Mode Selector Option Owned-Execution Deck Title/Selection Policy Preview"); + Surface.bHasSelector = OptionMenuSurface.bHasSelector; + + if (!SelectorId.IsEmpty()) + { + Surface.Headline = OptionMenuSurface.SelectorLabel.IsEmpty() + ? FString::Printf( + TEXT("Generated Mode Selector Option Owned-Execution Deck Title/Selection Policy Preview: %s"), + *SelectorId + ) + : FString::Printf( + TEXT("Generated Mode Selector Option Owned-Execution Deck Title/Selection Policy Preview: %s"), + *OptionMenuSurface.SelectorLabel + ); + } + + if (SelectorId.IsEmpty()) + { + Surface.StatusLine = + TEXT("Generated-mode selector option owned-execution deck title/selection policy preview requires a selector id from the active selector roster."); + Surface.DeckTitleSummaryLine = TEXT("Deck title: n/a"); + Surface.SelectionPolicySummaryLine = TEXT("Selection policy: n/a"); + Surface.CurrentSelectionDeckLine = TEXT("Current selection: n/a"); + Surface.DetailLine = OptionLaunchRequestPreviewSurface.StatusLine.IsEmpty() + ? TEXT("Launch-request preview: n/a") + : FString::Printf( + TEXT("Launch-request preview: %s"), + *OptionLaunchRequestPreviewSurface.StatusLine + ); + return Surface; + } + + const FHyperTwistTrainingImportedRuntimeSurface& RuntimeSurface = + RunState.ActiveDeck.ImportedRuntimeSurface; + const bool bHasGeneratedModeSurface = RuntimeSurface.IsStructurallyValid() + && RuntimeSurface.SurfaceKind.Equals(TEXT("generated-mode"), ESearchCase::IgnoreCase); + + const auto DescribeSelectionPolicy = []( + const EHyperTwistTrainingSelectionPolicy SelectionPolicy + ) -> FString + { + switch (SelectionPolicy) + { + case EHyperTwistTrainingSelectionPolicy::Uniform: + return TEXT("Uniform"); + case EHyperTwistTrainingSelectionPolicy::Weighted: + return TEXT("Weighted"); + case EHyperTwistTrainingSelectionPolicy::Spaced: + return TEXT("Spaced"); + case EHyperTwistTrainingSelectionPolicy::Scripted: + return TEXT("Scripted"); + default: + return TEXT("Unknown"); + } + }; + + const auto BuildDeckTitleLine = [](const FString& DeckTitle) -> FString + { + return DeckTitle.IsEmpty() + ? TEXT("Deck title on execute: empty") + : FString::Printf(TEXT("Deck title on execute: %s"), *DeckTitle); + }; + + const auto BuildSelectionPolicyLine = [&DescribeSelectionPolicy]( + const EHyperTwistTrainingSelectionPolicy SelectionPolicy + ) -> FString + { + return FString::Printf( + TEXT("Selection policy on execute: %s"), + *DescribeSelectionPolicy(SelectionPolicy) + ); + }; + + const auto IncrementSelectionPolicyCounts = [&Surface]( + const EHyperTwistTrainingSelectionPolicy SelectionPolicy + ) + { + switch (SelectionPolicy) + { + case EHyperTwistTrainingSelectionPolicy::Uniform: + ++Surface.UniformSelectionPolicyOptionCount; + break; + case EHyperTwistTrainingSelectionPolicy::Weighted: + ++Surface.WeightedSelectionPolicyOptionCount; + break; + case EHyperTwistTrainingSelectionPolicy::Spaced: + ++Surface.SpacedSelectionPolicyOptionCount; + break; + case EHyperTwistTrainingSelectionPolicy::Scripted: + ++Surface.ScriptedSelectionPolicyOptionCount; + break; + default: + break; + } + }; + + if (!RunState.IsStructurallyValid() || !bHasGeneratedModeSurface) + { + Surface.StatusLine = OptionMenuSurface.StatusLine.IsEmpty() + ? TEXT("Generated-mode selector option owned-execution deck title/selection policy preview is not currently available.") + : OptionMenuSurface.StatusLine; + Surface.DeckTitleSummaryLine = TEXT("Deck title: n/a"); + Surface.SelectionPolicySummaryLine = TEXT("Selection policy: n/a"); + Surface.CurrentSelectionDeckLine = OptionMenuSurface.bHasUnresolvedSelectionDetail + ? OptionMenuSurface.UnresolvedSelectionLine + : (OptionMenuSurface.bHasSelection + ? TEXT("Current selection remains cached, but no active owned-execution deck title/selection policy preview is available.") + : TEXT("Current selection: unresolved")); + Surface.DetailLine = FString::Printf( + TEXT("Launch-request preview: %s"), + OptionLaunchRequestPreviewSurface.StatusLine.IsEmpty() + ? TEXT("n/a") + : *OptionLaunchRequestPreviewSurface.StatusLine + ); + return Surface; + } + + for (const FHyperTwistTrainingCoachGeneratedModeSelectorOptionLaunchRequestPreviewEntrySurface& + PreviewEntry : OptionLaunchRequestPreviewSurface.OptionEntries) + { + FHyperTwistTrainingCoachGeneratedModeSelectorOptionOwnedExecutionDeckTitleSelectionPolicyPreviewEntrySurface + Entry; + Entry.OptionId = PreviewEntry.OptionId; + Entry.Label = PreviewEntry.Label; + Entry.OptionValue = PreviewEntry.OptionValue; + Entry.LaunchRequestPreviewKind = PreviewEntry.LaunchRequestPreviewKind; + Entry.bMatchesCurrentSelection = PreviewEntry.bMatchesCurrentSelection; + Entry.bStartsOwnedCaseImmediately = PreviewEntry.bStartsOwnedCaseImmediately; + + switch (PreviewEntry.LaunchRequestPreviewKind) + { + case EHyperTwistTrainingCoachGeneratedModeOptionLaunchRequestPreviewKind:: + DirectOwnedCaseStart: + Entry.PreviewDeckTitle = RunState.ActiveDeck.Title; + Entry.PreviewSelectionPolicy = EHyperTwistTrainingSelectionPolicy::Scripted; + Entry.PreviewSelectionPolicyLabel = + DescribeSelectionPolicy(Entry.PreviewSelectionPolicy); + Entry.bHasExactDeckTitle = true; + Entry.bHasExactSelectionPolicy = true; + Entry.bUsesActiveDeckTitle = true; + Entry.DeckTitleLine = BuildDeckTitleLine(Entry.PreviewDeckTitle); + Entry.DeckTitleSourceLine = Entry.PreviewDeckTitle.IsEmpty() + ? TEXT("Deck title source: exact active deck title retained on direct covered-case start, but the active deck title is currently empty.") + : TEXT("Deck title source: exact active deck title retained on direct covered-case start."); + Entry.SelectionPolicyLine = + BuildSelectionPolicyLine(Entry.PreviewSelectionPolicy); + Entry.SelectionPolicySourceLine = + TEXT("Selection policy source: direct covered-case start resets the owned execution deck to scripted selection."); + Entry.PreviewStabilityLine = + TEXT("Preview stability: exact direct owned-execution deck container preview from covered-case launch."); + Entry.bCanPreview = true; + ++Surface.DirectExactOptionCount; + ++Surface.RetainedActiveDeckTitleOptionCount; + IncrementSelectionPolicyCounts(Entry.PreviewSelectionPolicy); + break; + + case EHyperTwistTrainingCoachGeneratedModeOptionLaunchRequestPreviewKind:: + StoredRequestReusable: + case EHyperTwistTrainingCoachGeneratedModeOptionLaunchRequestPreviewKind:: + RefreshRequired: + { + Entry.PreviewSelectionPolicy = EHyperTwistTrainingSelectionPolicy::Scripted; + Entry.PreviewSelectionPolicyLabel = + DescribeSelectionPolicy(Entry.PreviewSelectionPolicy); + Entry.bHasExactDeckTitle = true; + Entry.bHasExactSelectionPolicy = true; + + if (!RunState.ActiveDeck.Title.IsEmpty()) + { + Entry.PreviewDeckTitle = RunState.ActiveDeck.Title; + Entry.bUsesActiveDeckTitle = true; + Entry.DeckTitleSourceLine = + TEXT("Deck title source: exact source-deck title retained on generated owned execution."); + ++Surface.RetainedActiveDeckTitleOptionCount; + } + else if (!PreviewEntry.HypotheticalSurfaceLabel.IsEmpty()) + { + Entry.PreviewDeckTitle = PreviewEntry.HypotheticalSurfaceLabel; + Entry.bUsesGeneratedSurfaceLabelFallback = true; + Entry.DeckTitleSourceLine = + TEXT("Deck title source: exact generated surface label fallback from the hypothetical execute launch config."); + ++Surface.SurfaceLabelFallbackOptionCount; + } + else if (!RuntimeSurface.SurfaceLabel.IsEmpty()) + { + Entry.PreviewDeckTitle = RuntimeSurface.SurfaceLabel; + Entry.bUsesGeneratedSurfaceLabelFallback = true; + Entry.DeckTitleSourceLine = + TEXT("Deck title source: exact active runtime-surface label fallback for generated owned execution."); + ++Surface.SurfaceLabelFallbackOptionCount; + } + else + { + Entry.PreviewDeckTitle = TEXT("Generated mode deck"); + Entry.bUsesGeneratedModeDefaultTitle = true; + Entry.DeckTitleSourceLine = + TEXT("Deck title source: exact generated-mode default title because no retained deck title or surface label is available."); + ++Surface.GeneratedModeDefaultTitleOptionCount; + } + + Entry.DeckTitleLine = BuildDeckTitleLine(Entry.PreviewDeckTitle); + Entry.SelectionPolicyLine = + BuildSelectionPolicyLine(Entry.PreviewSelectionPolicy); + Entry.SelectionPolicySourceLine = + TEXT("Selection policy source: generated owned execution resets the owned execution deck to scripted selection."); + Entry.PreviewStabilityLine = + PreviewEntry.LaunchRequestPreviewKind + == EHyperTwistTrainingCoachGeneratedModeOptionLaunchRequestPreviewKind:: + StoredRequestReusable + ? TEXT("Preview stability: exact while execute reuses the current stored request.") + : TEXT("Preview stability: exact even though execute still needs launch-request refresh."); + Entry.bCanPreview = true; + ++Surface.GeneratedExactOptionCount; + IncrementSelectionPolicyCounts(Entry.PreviewSelectionPolicy); + break; + } + + case EHyperTwistTrainingCoachGeneratedModeOptionLaunchRequestPreviewKind:: + UnresolvedAfterChoice: + Entry.DeckTitleLine = + TEXT("Deck title on execute: blocked until every required selector is resolved."); + Entry.DeckTitleSourceLine = + TEXT("Deck title source: blocked while selector coverage remains incomplete."); + Entry.SelectionPolicyLine = + TEXT("Selection policy on execute: blocked until every required selector is resolved."); + Entry.SelectionPolicySourceLine = + TEXT("Selection policy source: blocked while selector coverage remains incomplete."); + Entry.PreviewStabilityLine = + TEXT("Preview stability: blocked while selector coverage remains incomplete."); + Entry.bPreviewBlocked = true; + Entry.bCanPreview = true; + ++Surface.BlockedOptionCount; + break; + + case EHyperTwistTrainingCoachGeneratedModeOptionLaunchRequestPreviewKind:: + PayloadUnavailable: + Entry.DeckTitleLine = + TEXT("Deck title on execute: blocked until a structurally valid launch payload can be derived."); + Entry.DeckTitleSourceLine = + TEXT("Deck title source: blocked while the hypothetical generated-mode payload remains structurally invalid."); + Entry.SelectionPolicyLine = + TEXT("Selection policy on execute: blocked until a structurally valid launch payload can be derived."); + Entry.SelectionPolicySourceLine = + TEXT("Selection policy source: blocked while the hypothetical generated-mode payload remains structurally invalid."); + Entry.PreviewStabilityLine = + TEXT("Preview stability: blocked while the hypothetical generated-mode payload remains structurally invalid."); + Entry.bPreviewBlocked = true; + Entry.bCanPreview = true; + ++Surface.BlockedOptionCount; + break; + + case EHyperTwistTrainingCoachGeneratedModeOptionLaunchRequestPreviewKind::Unavailable: + case EHyperTwistTrainingCoachGeneratedModeOptionLaunchRequestPreviewKind::None: + default: + Entry.DeckTitleLine = TEXT("Deck title on execute: unavailable"); + Entry.DeckTitleSourceLine = TEXT("Deck title source: unavailable"); + Entry.SelectionPolicyLine = TEXT("Selection policy on execute: unavailable"); + Entry.SelectionPolicySourceLine = TEXT("Selection policy source: unavailable"); + Entry.PreviewStabilityLine = TEXT("Preview stability: unavailable"); + Entry.bCanPreview = false; + ++Surface.UnavailableOptionCount; + break; + } + + Entry.DetailLine = PreviewEntry.DetailLine.IsEmpty() + ? Entry.PreviewStabilityLine + : FString::Printf( + TEXT("%s | %s"), + *Entry.PreviewStabilityLine, + *PreviewEntry.DetailLine + ); + + if (Entry.bMatchesCurrentSelection) + { + Surface.bCurrentSelectionHasExactDeckTitle = Entry.bHasExactDeckTitle; + Surface.bCurrentSelectionHasExactSelectionPolicy = + Entry.bHasExactSelectionPolicy; + Surface.bCurrentSelectionUsesActiveDeckTitle = Entry.bUsesActiveDeckTitle; + Surface.bCurrentSelectionUsesGeneratedSurfaceLabelFallback = + Entry.bUsesGeneratedSurfaceLabelFallback; + Surface.bCurrentSelectionUsesGeneratedModeDefaultTitle = + Entry.bUsesGeneratedModeDefaultTitle; + Surface.CurrentSelectionDeckLine = FString::Printf( + TEXT("%s | %s | %s"), + *Entry.DeckTitleLine, + *Entry.SelectionPolicyLine, + *Entry.PreviewStabilityLine + ); + } + + Surface.OptionEntries.Add(MoveTemp(Entry)); + } + + Surface.bHasOwnedExecutionDeckTitleSelectionPolicyPreviewOptions = + Surface.OptionEntries.Num() > 0; + Surface.bHasDirectDeckTitleSelectionPolicyPreviewOptions = + Surface.DirectExactOptionCount > 0; + Surface.bHasGeneratedDeckTitleSelectionPolicyPreviewOptions = + Surface.GeneratedExactOptionCount > 0; + Surface.bHasBlockedDeckTitleSelectionPolicyPreviewOptions = + Surface.BlockedOptionCount > 0; + + Surface.DeckTitleSummaryLine = Surface.OptionEntries.Num() > 0 + ? FString::Printf( + TEXT("Deck title: %d retained active-deck | %d surface-label fallback | %d generated default | %d blocked | %d unavailable"), + Surface.RetainedActiveDeckTitleOptionCount, + Surface.SurfaceLabelFallbackOptionCount, + Surface.GeneratedModeDefaultTitleOptionCount, + Surface.BlockedOptionCount, + Surface.UnavailableOptionCount + ) + : TEXT("Deck title: no imported options"); + Surface.SelectionPolicySummaryLine = Surface.OptionEntries.Num() > 0 + ? FString::Printf( + TEXT("Selection policy: %d scripted | %d weighted | %d uniform | %d spaced | %d blocked | %d unavailable"), + Surface.ScriptedSelectionPolicyOptionCount, + Surface.WeightedSelectionPolicyOptionCount, + Surface.UniformSelectionPolicyOptionCount, + Surface.SpacedSelectionPolicyOptionCount, + Surface.BlockedOptionCount, + Surface.UnavailableOptionCount + ) + : TEXT("Selection policy: no imported options"); + + if (Surface.CurrentSelectionDeckLine.IsEmpty()) + { + if (OptionMenuSurface.bHasUnresolvedSelectionDetail) + { + Surface.CurrentSelectionDeckLine = OptionMenuSurface.UnresolvedSelectionLine; + } + else if (OptionMenuSurface.bHasSelection) + { + Surface.CurrentSelectionDeckLine = + TEXT("Current selection has no additional owned-execution deck container preview."); + } + else if (OptionMenuSurface.bHasSelector) + { + Surface.CurrentSelectionDeckLine = TEXT("Current selection: unresolved"); + } + else + { + Surface.CurrentSelectionDeckLine = TEXT("Current selection: n/a"); + } + } + + if (OptionMenuSurface.bHasSelector && Surface.OptionEntries.Num() > 0) + { + Surface.StatusLine = FString::Printf( + TEXT("Generated-mode selector option owned-execution deck title/selection policy preview exposes %d direct exact option%s, %d generated exact option%s, %d blocked option%s, and %d unavailable option%s."), + Surface.DirectExactOptionCount, + Surface.DirectExactOptionCount == 1 ? TEXT("") : TEXT("s"), + Surface.GeneratedExactOptionCount, + Surface.GeneratedExactOptionCount == 1 ? TEXT("") : TEXT("s"), + Surface.BlockedOptionCount, + Surface.BlockedOptionCount == 1 ? TEXT("") : TEXT("s"), + Surface.UnavailableOptionCount, + Surface.UnavailableOptionCount == 1 ? TEXT("") : TEXT("s") + ); + } + else if (OptionMenuSurface.bHasSelector) + { + Surface.StatusLine = + TEXT("Generated-mode selector option owned-execution deck title/selection policy preview is active, but this selector does not expose imported options."); + } + else + { + Surface.StatusLine = OptionMenuSurface.StatusLine.IsEmpty() + ? TEXT("Generated-mode selector option owned-execution deck title/selection policy preview is not currently available.") + : OptionMenuSurface.StatusLine; + } + + const FString SelectorSummary = Surface.SelectorLabel.IsEmpty() + ? (Surface.SelectorId.IsEmpty() ? FString(TEXT("n/a")) : Surface.SelectorId) + : Surface.SelectorLabel; + const FString BindingSummary = Surface.BindingLabel.IsEmpty() + ? FString(TEXT("n/a")) + : Surface.BindingLabel; + Surface.DetailLine = FString::Printf( + TEXT("Launch-request preview: %s | Selector: %s | Binding: %s"), + OptionLaunchRequestPreviewSurface.StatusLine.IsEmpty() + ? TEXT("n/a") + : *OptionLaunchRequestPreviewSurface.StatusLine, + *SelectorSummary, + *BindingSummary + ); + + 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 43ef274..b4e7948 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingPanelWidget.cpp +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingPanelWidget.cpp @@ -967,6 +967,25 @@ UHyperTwistTrainingPanelWidget::GetDisplayedCoachGeneratedModeSelectorOptionOwne ); } +FHyperTwistTrainingCoachGeneratedModeSelectorOptionOwnedExecutionDeckTitleSelectionPolicyPreviewSurface +UHyperTwistTrainingPanelWidget::GetDisplayedCoachGeneratedModeSelectorOptionOwnedExecutionDeckTitleSelectionPolicyPreviewSurface( + const FString& SelectorId +) const +{ + const FHyperTwistTrainingCoachGeneratedModeSelectorOptionMenuSurface OptionMenuSurface = + GetDisplayedCoachGeneratedModeSelectorOptionMenuSurface(SelectorId); + const FHyperTwistTrainingCoachGeneratedModeSelectorOptionLaunchRequestPreviewSurface + OptionLaunchRequestPreviewSurface = + GetDisplayedCoachGeneratedModeSelectorOptionLaunchRequestPreviewSurface(SelectorId); + return UHyperTwistTrainingCoachLibrary:: + DeriveCoachGeneratedModeSelectorOptionOwnedExecutionDeckTitleSelectionPolicyPreviewSurface( + 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 28d28a2..cc7334a 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingSessionActor.cpp +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingSessionActor.cpp @@ -948,6 +948,25 @@ AHyperTwistTrainingSessionActor::GetDisplayedCoachGeneratedModeSelectorOptionOwn ); } +FHyperTwistTrainingCoachGeneratedModeSelectorOptionOwnedExecutionDeckTitleSelectionPolicyPreviewSurface +AHyperTwistTrainingSessionActor::GetDisplayedCoachGeneratedModeSelectorOptionOwnedExecutionDeckTitleSelectionPolicyPreviewSurface( + const FString& SelectorId +) const +{ + const FHyperTwistTrainingCoachGeneratedModeSelectorOptionMenuSurface OptionMenuSurface = + GetDisplayedCoachGeneratedModeSelectorOptionMenuSurface(SelectorId); + const FHyperTwistTrainingCoachGeneratedModeSelectorOptionLaunchRequestPreviewSurface + OptionLaunchRequestPreviewSurface = + GetDisplayedCoachGeneratedModeSelectorOptionLaunchRequestPreviewSurface(SelectorId); + return UHyperTwistTrainingCoachLibrary:: + DeriveCoachGeneratedModeSelectorOptionOwnedExecutionDeckTitleSelectionPolicyPreviewSurface( + 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 fccecaf..e976241 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingCoachLibrary.h +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingCoachLibrary.h @@ -827,4 +827,14 @@ public: const FHyperTwistTrainingCoachGeneratedModeSelectorOptionLaunchRequestPreviewSurface& OptionLaunchRequestPreviewSurface ); + + UFUNCTION(BlueprintPure, Category = "HyperTwist|Training|Coach") + static FHyperTwistTrainingCoachGeneratedModeSelectorOptionOwnedExecutionDeckTitleSelectionPolicyPreviewSurface + DeriveCoachGeneratedModeSelectorOptionOwnedExecutionDeckTitleSelectionPolicyPreviewSurface( + 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 18adf61..2fa1126 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingPanelWidget.h +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingPanelWidget.h @@ -326,6 +326,12 @@ public: const FString& SelectorId ) const; + UFUNCTION(BlueprintPure, Category = "HyperTwist|Training|Coach") + FHyperTwistTrainingCoachGeneratedModeSelectorOptionOwnedExecutionDeckTitleSelectionPolicyPreviewSurface + GetDisplayedCoachGeneratedModeSelectorOptionOwnedExecutionDeckTitleSelectionPolicyPreviewSurface( + 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 9a08940..ebd475e 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingSessionActor.h +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingSessionActor.h @@ -307,6 +307,12 @@ public: const FString& SelectorId ) const; + UFUNCTION(BlueprintPure, Category = "HyperTwist|Training|Coach") + FHyperTwistTrainingCoachGeneratedModeSelectorOptionOwnedExecutionDeckTitleSelectionPolicyPreviewSurface + GetDisplayedCoachGeneratedModeSelectorOptionOwnedExecutionDeckTitleSelectionPolicyPreviewSurface( + 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 1374c4f..248e2ab 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingTypes.h +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingTypes.h @@ -11125,3 +11125,197 @@ struct FHyperTwistTrainingCoachGeneratedModeSelectorOptionOwnedExecutionProvenan || bHasOwnedExecutionProvenancePreviewOptions; } }; + +USTRUCT(BlueprintType) +struct FHyperTwistTrainingCoachGeneratedModeSelectorOptionOwnedExecutionDeckTitleSelectionPolicyPreviewEntrySurface +{ + GENERATED_BODY() + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString OptionId; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString Label; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString OptionValue; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + EHyperTwistTrainingCoachGeneratedModeOptionLaunchRequestPreviewKind LaunchRequestPreviewKind = + EHyperTwistTrainingCoachGeneratedModeOptionLaunchRequestPreviewKind::None; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString DeckTitleLine; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString DeckTitleSourceLine; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString SelectionPolicyLine; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString SelectionPolicySourceLine; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString PreviewStabilityLine; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString DetailLine; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString PreviewDeckTitle; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + EHyperTwistTrainingSelectionPolicy PreviewSelectionPolicy = + EHyperTwistTrainingSelectionPolicy::Weighted; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString PreviewSelectionPolicyLabel; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bMatchesCurrentSelection = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bStartsOwnedCaseImmediately = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bHasExactDeckTitle = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bHasExactSelectionPolicy = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bUsesActiveDeckTitle = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bUsesGeneratedSurfaceLabelFallback = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bUsesGeneratedModeDefaultTitle = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bPreviewBlocked = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bCanPreview = false; + + bool IsStructurallyValid() const + { + return !OptionId.IsEmpty() + || !Label.IsEmpty() + || !DeckTitleLine.IsEmpty() + || !SelectionPolicyLine.IsEmpty() + || LaunchRequestPreviewKind + != EHyperTwistTrainingCoachGeneratedModeOptionLaunchRequestPreviewKind::None + || bCanPreview; + } +}; + +USTRUCT(BlueprintType) +struct FHyperTwistTrainingCoachGeneratedModeSelectorOptionOwnedExecutionDeckTitleSelectionPolicyPreviewSurface +{ + 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 DeckTitleSummaryLine; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString SelectionPolicySummaryLine; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString CurrentSelectionDeckLine; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString DetailLine; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + TArray + OptionEntries; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + int32 DirectExactOptionCount = 0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + int32 GeneratedExactOptionCount = 0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + int32 BlockedOptionCount = 0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + int32 UnavailableOptionCount = 0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + int32 RetainedActiveDeckTitleOptionCount = 0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + int32 SurfaceLabelFallbackOptionCount = 0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + int32 GeneratedModeDefaultTitleOptionCount = 0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + int32 UniformSelectionPolicyOptionCount = 0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + int32 WeightedSelectionPolicyOptionCount = 0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + int32 SpacedSelectionPolicyOptionCount = 0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + int32 ScriptedSelectionPolicyOptionCount = 0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bHasSelector = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bHasOwnedExecutionDeckTitleSelectionPolicyPreviewOptions = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bHasDirectDeckTitleSelectionPolicyPreviewOptions = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bHasGeneratedDeckTitleSelectionPolicyPreviewOptions = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bHasBlockedDeckTitleSelectionPolicyPreviewOptions = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bCurrentSelectionHasExactDeckTitle = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bCurrentSelectionHasExactSelectionPolicy = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bCurrentSelectionUsesActiveDeckTitle = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bCurrentSelectionUsesGeneratedSurfaceLabelFallback = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bCurrentSelectionUsesGeneratedModeDefaultTitle = false; + + bool IsStructurallyValid() const + { + return !StatusLine.IsEmpty() + || !SelectorId.IsEmpty() + || OptionEntries.Num() > 0 + || bHasSelector + || bHasOwnedExecutionDeckTitleSelectionPolicyPreviewOptions; + } +}; diff --git a/docs/HYPERTWIST_ROADMAP_OVERHAUL_EXPANSION_GUIDE.md b/docs/HYPERTWIST_ROADMAP_OVERHAUL_EXPANSION_GUIDE.md index 4752d16..2ef5355 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 source-attribution / tag provenance preview surface over the already-cached selector/runtime/request state, so gameplay/Blueprint callers can render resulting case/deck attribution plus exact-or-template tag posture for a chosen option without rebuilding generated deck composition rules directly - - the current next bounded packet is to expose one thin generated-mode selector option owned execution deck-title / selection-policy preview surface on those same two consumer surfaces so gameplay/Blueprint callers can render resulting owned execution container identity and selection-policy posture for a chosen option without rebuilding generated deck composition rules directly + - 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 deck-title / selection-policy preview surface over the already-cached selector/runtime/request state, so gameplay/Blueprint callers can render resulting owned execution container identity and selection-policy posture for a chosen option without rebuilding generated deck composition rules directly + - the current next bounded packet is to expose one thin generated-mode selector option owned execution deck-id / runtime-surface retention preview surface on those same two consumer surfaces so gameplay/Blueprint callers can render retained container id and imported runtime-surface posture for a chosen option without rebuilding generated deck composition rules directly - 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_DECK_TITLE_SELECTION_POLICY_PREVIEW_SURFACE_PACKET_2026-05-08.md b/docs/arch/HYPERTWIST_PHASE4_GAMEPLAY_GENERATED_MODE_SELECTOR_OPTION_OWNED_EXECUTION_DECK_TITLE_SELECTION_POLICY_PREVIEW_SURFACE_PACKET_2026-05-08.md new file mode 100644 index 0000000..3cbca4a --- /dev/null +++ b/docs/arch/HYPERTWIST_PHASE4_GAMEPLAY_GENERATED_MODE_SELECTOR_OPTION_OWNED_EXECUTION_DECK_TITLE_SELECTION_POLICY_PREVIEW_SURFACE_PACKET_2026-05-08.md @@ -0,0 +1,102 @@ +# HyperTwist Phase 4 gameplay generated-mode selector option owned execution deck-title / selection-policy preview surface packet + +Created on `2026-05-08` + +Status: + +- first-party HyperTwist packet +- bounded Phase `4` gameplay-facing consumer slice + +## Purpose + +This packet closes the next thin gameplay-facing consumer seam above the panel-widget and session-actor generated-mode selector option owned execution source-attribution / tag provenance preview surface by exposing one parameterized owned execution deck container preview surface for a chosen selector id. + +The open tasks were: + +- stop forcing gameplay / Blueprint callers to rebuild owned execution deck title fallback and selection-policy rules just to explain what container a chosen imported option would actually run inside +- expose direct covered-case deck-title retention plus generated owned-execution deck-title fallback on the same thin consumer lane +- expose resulting owned execution selection-policy posture without widening into backend execution behavior + +It is not: + +- a selector-dispatch behavior change +- a generated-mode launch-request persistence behavior change +- a generated-mode execution deck synthesis change +- a broader gameplay UI composition pass + +## Scope + +Bounded lane: + +- add first-party owned execution deck-title / selection-policy preview surfaces in shared training types +- add a coach-library helper that projects resulting owned execution deck title and selection policy over the already-landed launch-request preview surface plus active run state +- expose that parameterized deck container preview surface on `HyperTwistTrainingPanelWidget` and `HyperTwistTrainingSessionActor` +- keep the derivation presentation-only and rooted in the already-owned selector/runtime/request surfaces + +Out of scope: + +- changing selector-choice apply behavior +- changing stored launch-request creation, refresh, or execute behavior +- changing generated-mode execution deck synthesis +- widening into dashboard-only or broader gameplay composition work + +## Why this was the right next packet + +Before this slice: + +- gameplay-facing panel/session consumers already exposed truthful owned case identity, prompt presentation, delivery mode, reveal posture, target time, execution scope, and provenance posture for imported selector options +- callers could tell what owned execution would run and where it came from + +But one thin consumer seam was still open: + +- callers still had to reconstruct owned execution deck title fallback and forced scripted-selection behavior from direct covered-case rules, active deck state, and generated owned-execution synthesis rules if they wanted to explain the resulting execution container + +That meant: + +- case-level preview and provenance were already thin and reusable +- but deck container identity and policy posture still leaked generated deck composition rules into callers + +So the next bounded move was: + +- keep the already-landed owned execution provenance preview surface unchanged in behavior +- reuse the already-cached launch-request preview surface and active run state to expose one narrow owned execution deck-title / selection-policy preview surface above it + +## What landed + +Primary code changes: + +- `UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingTypes.h` + - added `FHyperTwistTrainingCoachGeneratedModeSelectorOptionOwnedExecutionDeckTitleSelectionPolicyPreviewEntrySurface` + - added `FHyperTwistTrainingCoachGeneratedModeSelectorOptionOwnedExecutionDeckTitleSelectionPolicyPreviewSurface` +- `UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingCoachLibrary.h` +- `UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingCoachLibrary.cpp` + - added `DeriveCoachGeneratedModeSelectorOptionOwnedExecutionDeckTitleSelectionPolicyPreviewSurface(...)` + - deck container preview is derived from cached run state plus the already-landed launch-request preview surface +- `UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingPanelWidget.h` +- `UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingPanelWidget.cpp` + - added `GetDisplayedCoachGeneratedModeSelectorOptionOwnedExecutionDeckTitleSelectionPolicyPreviewSurface(const FString& SelectorId)` +- `UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingSessionActor.h` +- `UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingSessionActor.cpp` + - added `GetDisplayedCoachGeneratedModeSelectorOptionOwnedExecutionDeckTitleSelectionPolicyPreviewSurface(const FString& SelectorId)` + +Surface behavior: + +- reports exact direct covered-case deck-title retention and exact scripted selection-policy posture for selector options that dispatch straight to owned cases +- reports exact generated owned-execution deck title from retained source-deck title or generated surface-label fallback, plus exact scripted selection-policy posture for previewable generated-mode options +- keeps reusable stored-request cases and refresh-required generated cases exact because deck title and selection policy do not depend on the eventual request id +- keeps unresolved or payload-invalid choices inspectable as blocked deck-container preview states instead of collapsing them into a generic unavailable branch +- keeps the surface presentation-only and compatible with the already-landed launch / execution, request-alignment, stored-request delta, launch-request payload / refresh-preview, launch-request metadata / origin-notes preview, launch-request envelope / request-source preview, owned execution case / canonical-state preview, owned execution prompt preview, owned execution delivery preview, owned execution scope preview, and owned execution provenance preview surfaces + +## Validation + +Validation target: + +- full `Development Editor|Win64` build of `C:\HyperTwist\UnrealHyperTwist\UnrealHyperTwist.sln` + +Result: + +- passed with `0` warnings and `0` errors + +## Follow-on + +The next bounded packet on this same gameplay-facing consumer lane should stay thin and expose one generated-mode selector option owned execution deck-id / runtime-surface retention preview surface so callers can render retained container id and imported runtime-surface posture for a chosen option without rebuilding generated deck composition rules directly.