diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingCoachLibrary.cpp b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingCoachLibrary.cpp index cdb4983..49a508a 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingCoachLibrary.cpp +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingCoachLibrary.cpp @@ -10148,6 +10148,334 @@ UHyperTwistTrainingCoachLibrary:: return Surface; } +FHyperTwistTrainingCoachGeneratedModeSelectorOptionOwnedExecutionSplitPhaseNormalizationSummaryPreviewSurface +UHyperTwistTrainingCoachLibrary:: + DeriveCoachGeneratedModeSelectorOptionOwnedExecutionSplitPhaseNormalizationSummaryPreviewSurface( + const FHyperTwistTrainingRunState& RunState, + const FString& SelectorId, + const FHyperTwistTrainingCoachGeneratedModeSelectorOptionMenuSurface& OptionMenuSurface, + const FHyperTwistTrainingCoachGeneratedModeSelectorOptionLaunchRequestPreviewSurface& + OptionLaunchRequestPreviewSurface + ) +{ + const FHyperTwistTrainingCoachGeneratedModeSelectorOptionOwnedExecutionSplitPhasePhaseIdLabelFallbackProvenancePreviewSurface + LabelProvenancePreviewSurface = + DeriveCoachGeneratedModeSelectorOptionOwnedExecutionSplitPhasePhaseIdLabelFallbackProvenancePreviewSurface( + RunState, + SelectorId, + OptionMenuSurface, + OptionLaunchRequestPreviewSurface + ); + const FHyperTwistTrainingCoachGeneratedModeSelectorOptionOwnedExecutionSplitPhaseDuplicatePruningSurvivorProvenancePreviewSurface + DuplicatePruningPreviewSurface = + DeriveCoachGeneratedModeSelectorOptionOwnedExecutionSplitPhaseDuplicatePruningSurvivorProvenancePreviewSurface( + RunState, + SelectorId, + OptionMenuSurface, + OptionLaunchRequestPreviewSurface + ); + + FHyperTwistTrainingCoachGeneratedModeSelectorOptionOwnedExecutionSplitPhaseNormalizationSummaryPreviewSurface + Surface; + Surface.SelectorId = SelectorId; + Surface.SelectorLabel = OptionMenuSurface.SelectorLabel; + Surface.BindingLabel = OptionMenuSurface.BindingLabel; + Surface.Headline = + TEXT("Generated Mode Selector Option Owned-Execution Split-Phase Normalization Summary Preview"); + Surface.bHasSelector = OptionMenuSurface.bHasSelector; + + if (!SelectorId.IsEmpty()) + { + Surface.Headline = OptionMenuSurface.SelectorLabel.IsEmpty() + ? FString::Printf( + TEXT("Generated Mode Selector Option Owned-Execution Split-Phase Normalization Summary Preview: %s"), + *SelectorId + ) + : FString::Printf( + TEXT("Generated Mode Selector Option Owned-Execution Split-Phase Normalization Summary Preview: %s"), + *OptionMenuSurface.SelectorLabel + ); + } + + if (SelectorId.IsEmpty()) + { + Surface.StatusLine = + TEXT("Generated-mode selector option owned-execution split-phase normalization summary preview requires a selector id from the active selector roster."); + Surface.NormalizationSummaryLine = TEXT("Split-phase normalization summary: n/a"); + Surface.LabelFallbackSummaryLine = TEXT("Label-fallback summary: n/a"); + Surface.PreviewPostureSummaryLine = TEXT("Preview posture: n/a"); + Surface.CurrentSelectionNormalizationSummaryLine = TEXT("Current selection: n/a"); + Surface.DetailLine = !DuplicatePruningPreviewSurface.DetailLine.IsEmpty() + ? DuplicatePruningPreviewSurface.DetailLine + : LabelProvenancePreviewSurface.DetailLine; + return Surface; + } + + TMap LabelEntryIndexByOptionId; + for (int32 EntryIndex = 0; + EntryIndex < LabelProvenancePreviewSurface.OptionEntries.Num(); + ++EntryIndex) + { + LabelEntryIndexByOptionId.FindOrAdd( + LabelProvenancePreviewSurface.OptionEntries[EntryIndex].OptionId + ) = EntryIndex; + } + + const auto BuildBlockedLabelLine = []( + const FString& BlockReason + ) -> FString + { + return BlockReason.IsEmpty() + ? TEXT("Label provenance on execute: blocked") + : FString::Printf(TEXT("Label provenance on execute: %s"), *BlockReason); + }; + + const auto BuildFallbackLine = []( + const FString& FallbackReason + ) -> FString + { + return FallbackReason.IsEmpty() + ? TEXT("Phase-id fallback labels on execute: unavailable") + : FallbackReason; + }; + + for (const FHyperTwistTrainingCoachGeneratedModeSelectorOptionOwnedExecutionSplitPhaseDuplicatePruningSurvivorProvenancePreviewEntrySurface& + DuplicateEntry : DuplicatePruningPreviewSurface.OptionEntries) + { + const int32* LabelEntryIndex = LabelEntryIndexByOptionId.Find( + DuplicateEntry.OptionId + ); + const FHyperTwistTrainingCoachGeneratedModeSelectorOptionOwnedExecutionSplitPhasePhaseIdLabelFallbackProvenancePreviewEntrySurface* + LabelEntry = LabelEntryIndex != nullptr + ? &LabelProvenancePreviewSurface.OptionEntries[*LabelEntryIndex] + : nullptr; + + FHyperTwistTrainingCoachGeneratedModeSelectorOptionOwnedExecutionSplitPhaseNormalizationSummaryPreviewEntrySurface + Entry; + Entry.OptionId = DuplicateEntry.OptionId; + Entry.Label = DuplicateEntry.Label; + Entry.OptionValue = DuplicateEntry.OptionValue; + Entry.LaunchRequestPreviewKind = DuplicateEntry.LaunchRequestPreviewKind; + Entry.RawSplitPhaseCountLine = DuplicateEntry.RawSplitPhaseCountLine; + Entry.SurvivorCountLine = DuplicateEntry.SurvivorCountLine; + Entry.DuplicatePruningLine = DuplicateEntry.DuplicatePruningLine; + Entry.InvalidDefinitionLine = DuplicateEntry.InvalidDefinitionLine; + Entry.RawSplitPhaseCount = DuplicateEntry.RawSplitPhaseCount; + Entry.SurvivorPhaseCount = DuplicateEntry.SurvivorPhaseCount; + Entry.DuplicatePrunedPhaseCount = DuplicateEntry.DuplicatePrunedPhaseCount; + Entry.InvalidRawDefinitionCount = DuplicateEntry.InvalidRawDefinitionCount; + Entry.bMatchesCurrentSelection = DuplicateEntry.bMatchesCurrentSelection; + Entry.bStartsOwnedCaseImmediately = DuplicateEntry.bStartsOwnedCaseImmediately; + Entry.bHasDuplicatePrunedPhases = DuplicateEntry.bHasDuplicatePrunedPhases; + Entry.bHasInvalidRawDefinitions = DuplicateEntry.bHasInvalidRawDefinitions; + Entry.bPreviewBlocked = DuplicateEntry.bPreviewBlocked; + Entry.bCanPreview = DuplicateEntry.bCanPreview; + + if (LabelEntry != nullptr) + { + Entry.LabelProvenanceLine = LabelEntry->LabelProvenanceLine; + Entry.FallbackPhaseIdLine = LabelEntry->FallbackPhaseIdLine; + Entry.ExplicitLabelPhaseCount = LabelEntry->ExplicitLabelPhaseCount; + Entry.FallbackLabelPhaseCount = LabelEntry->FallbackLabelPhaseCount; + Entry.bHasFallbackLabels = LabelEntry->bHasFallbackLabels; + Entry.bHasExactNormalizationSummaryPreview = + DuplicateEntry.bHasExactSplitPhaseDuplicatePruningPreview + && LabelEntry->bHasExactSplitPhaseLabelProvenancePreview; + Entry.bRetainsActiveDeckSplitPhaseNormalizationSummary = + DuplicateEntry.bRetainsActiveDeckSplitPhaseDuplicatePruningProvenance + && LabelEntry->bRetainsActiveDeckSplitPhaseLabelProvenance; + } + else + { + Entry.LabelProvenanceLine = DuplicateEntry.bPreviewBlocked + ? BuildBlockedLabelLine(TEXT("blocked while the split-phase normalization preview cannot be fully derived")) + : TEXT("Label provenance on execute: unavailable"); + Entry.FallbackPhaseIdLine = DuplicateEntry.bPreviewBlocked + ? TEXT("Phase-id fallback labels on execute: blocked while the split-phase normalization preview cannot be fully derived.") + : TEXT("Phase-id fallback labels on execute: unavailable"); + } + + Entry.PreviewPostureLine = DuplicateEntry.SplitPhaseSourceLine.IsEmpty() + ? DuplicateEntry.PreviewStabilityLine + : FString::Printf( + TEXT("Preview posture on execute: %s | %s"), + *DuplicateEntry.PreviewStabilityLine, + *DuplicateEntry.SplitPhaseSourceLine + ); + Entry.DetailLine = FString::Printf( + TEXT("%s | %s | %s"), + *Entry.PreviewPostureLine, + *Entry.DuplicatePruningLine, + *Entry.LabelProvenanceLine + ); + + switch (Entry.LaunchRequestPreviewKind) + { + case EHyperTwistTrainingCoachGeneratedModeOptionLaunchRequestPreviewKind:: + DirectOwnedCaseStart: + ++Surface.DirectExactOptionCount; + break; + + case EHyperTwistTrainingCoachGeneratedModeOptionLaunchRequestPreviewKind:: + StoredRequestReusable: + case EHyperTwistTrainingCoachGeneratedModeOptionLaunchRequestPreviewKind:: + RefreshRequired: + ++Surface.GeneratedExactOptionCount; + break; + + case EHyperTwistTrainingCoachGeneratedModeOptionLaunchRequestPreviewKind:: + UnresolvedAfterChoice: + case EHyperTwistTrainingCoachGeneratedModeOptionLaunchRequestPreviewKind:: + PayloadUnavailable: + ++Surface.BlockedOptionCount; + break; + + case EHyperTwistTrainingCoachGeneratedModeOptionLaunchRequestPreviewKind::Unavailable: + case EHyperTwistTrainingCoachGeneratedModeOptionLaunchRequestPreviewKind::None: + default: + ++Surface.UnavailableOptionCount; + break; + } + + Surface.RawSplitPhaseDefinitionCount += Entry.RawSplitPhaseCount; + Surface.SurvivorPhaseCount += Entry.SurvivorPhaseCount; + Surface.DuplicatePrunedPhaseCount += Entry.DuplicatePrunedPhaseCount; + Surface.InvalidRawDefinitionCount += Entry.InvalidRawDefinitionCount; + Surface.ExplicitLabelPhaseCount += Entry.ExplicitLabelPhaseCount; + Surface.FallbackLabelPhaseCount += Entry.FallbackLabelPhaseCount; + if (Entry.bHasFallbackLabels) + { + ++Surface.OptionsWithFallbackLabelCount; + } + if (Entry.bHasDuplicatePrunedPhases) + { + ++Surface.OptionsWithDuplicatePruningCount; + } + if (Entry.bHasInvalidRawDefinitions) + { + ++Surface.OptionsWithInvalidRawDefinitionsCount; + } + + if (Entry.bMatchesCurrentSelection) + { + Surface.bCurrentSelectionHasExactSplitPhaseNormalizationSummaryPreview = + Entry.bHasExactNormalizationSummaryPreview; + Surface.bCurrentSelectionRetainsActiveDeckSplitPhaseNormalizationSummary = + Entry.bRetainsActiveDeckSplitPhaseNormalizationSummary; + Surface.bCurrentSelectionHasFallbackLabels = Entry.bHasFallbackLabels; + Surface.bCurrentSelectionHasDuplicatePrunedPhases = + Entry.bHasDuplicatePrunedPhases; + Surface.bCurrentSelectionHasInvalidRawDefinitions = + Entry.bHasInvalidRawDefinitions; + Surface.CurrentSelectionNormalizationSummaryLine = FString::Printf( + TEXT("%s | %s | %s | %s | %s | %s"), + *Entry.RawSplitPhaseCountLine, + *Entry.SurvivorCountLine, + *Entry.DuplicatePruningLine, + *Entry.InvalidDefinitionLine, + *Entry.LabelProvenanceLine, + *BuildFallbackLine(Entry.FallbackPhaseIdLine) + ); + } + + Surface.OptionEntries.Add(MoveTemp(Entry)); + } + + Surface.bHasOwnedExecutionSplitPhaseNormalizationSummaryPreviewOptions = + Surface.OptionEntries.Num() > 0; + Surface.bHasDirectSplitPhaseNormalizationSummaryPreviewOptions = + Surface.DirectExactOptionCount > 0; + Surface.bHasGeneratedSplitPhaseNormalizationSummaryPreviewOptions = + Surface.GeneratedExactOptionCount > 0; + Surface.bHasBlockedSplitPhaseNormalizationSummaryPreviewOptions = + Surface.BlockedOptionCount > 0; + + Surface.NormalizationSummaryLine = Surface.OptionEntries.Num() > 0 + ? FString::Printf( + TEXT("Split-phase normalization summary: %d raw definitions | %d survivors | %d duplicate-pruned | %d invalid | %d blocked | %d unavailable"), + Surface.RawSplitPhaseDefinitionCount, + Surface.SurvivorPhaseCount, + Surface.DuplicatePrunedPhaseCount, + Surface.InvalidRawDefinitionCount, + Surface.BlockedOptionCount, + Surface.UnavailableOptionCount + ) + : TEXT("Split-phase normalization summary: no imported options"); + Surface.LabelFallbackSummaryLine = Surface.OptionEntries.Num() > 0 + ? FString::Printf( + TEXT("Label-fallback summary: %d explicit labels | %d phase-id fallbacks | %d options with fallback | %d options with duplicate-pruned rows | %d options with invalid rows"), + Surface.ExplicitLabelPhaseCount, + Surface.FallbackLabelPhaseCount, + Surface.OptionsWithFallbackLabelCount, + Surface.OptionsWithDuplicatePruningCount, + Surface.OptionsWithInvalidRawDefinitionsCount + ) + : TEXT("Label-fallback summary: no imported options"); + Surface.PreviewPostureSummaryLine = Surface.OptionEntries.Num() > 0 + ? FString::Printf( + TEXT("Preview posture: %d direct exact | %d generated exact | %d blocked | %d unavailable"), + Surface.DirectExactOptionCount, + Surface.GeneratedExactOptionCount, + Surface.BlockedOptionCount, + Surface.UnavailableOptionCount + ) + : TEXT("Preview posture: no imported options"); + + if (Surface.CurrentSelectionNormalizationSummaryLine.IsEmpty()) + { + if (OptionMenuSurface.bHasUnresolvedSelectionDetail) + { + Surface.CurrentSelectionNormalizationSummaryLine = + OptionMenuSurface.UnresolvedSelectionLine; + } + else if (OptionMenuSurface.bHasSelection) + { + Surface.CurrentSelectionNormalizationSummaryLine = + TEXT("Current selection has no additional owned-execution split-phase normalization summary preview."); + } + else if (OptionMenuSurface.bHasSelector) + { + Surface.CurrentSelectionNormalizationSummaryLine = + TEXT("Current selection: unresolved"); + } + else + { + Surface.CurrentSelectionNormalizationSummaryLine = + TEXT("Current selection: n/a"); + } + } + + if (OptionMenuSurface.bHasSelector && Surface.OptionEntries.Num() > 0) + { + Surface.StatusLine = FString::Printf( + TEXT("Generated-mode selector option owned-execution split-phase normalization summary 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 split-phase normalization summary preview is active, but this selector does not expose imported options."); + } + else + { + Surface.StatusLine = DuplicatePruningPreviewSurface.StatusLine; + } + + Surface.DetailLine = !DuplicatePruningPreviewSurface.DetailLine.IsEmpty() + ? DuplicatePruningPreviewSurface.DetailLine + : LabelProvenancePreviewSurface.DetailLine; + + 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 ab5367b..d32995c 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingPanelWidget.cpp +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingPanelWidget.cpp @@ -1119,6 +1119,25 @@ UHyperTwistTrainingPanelWidget::GetDisplayedCoachGeneratedModeSelectorOptionOwne ); } +FHyperTwistTrainingCoachGeneratedModeSelectorOptionOwnedExecutionSplitPhaseNormalizationSummaryPreviewSurface +UHyperTwistTrainingPanelWidget::GetDisplayedCoachGeneratedModeSelectorOptionOwnedExecutionSplitPhaseNormalizationSummaryPreviewSurface( + const FString& SelectorId +) const +{ + const FHyperTwistTrainingCoachGeneratedModeSelectorOptionMenuSurface OptionMenuSurface = + GetDisplayedCoachGeneratedModeSelectorOptionMenuSurface(SelectorId); + const FHyperTwistTrainingCoachGeneratedModeSelectorOptionLaunchRequestPreviewSurface + OptionLaunchRequestPreviewSurface = + GetDisplayedCoachGeneratedModeSelectorOptionLaunchRequestPreviewSurface(SelectorId); + return UHyperTwistTrainingCoachLibrary:: + DeriveCoachGeneratedModeSelectorOptionOwnedExecutionSplitPhaseNormalizationSummaryPreviewSurface( + 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 cb6ea1b..fffbf33 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingSessionActor.cpp +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingSessionActor.cpp @@ -1100,6 +1100,25 @@ AHyperTwistTrainingSessionActor::GetDisplayedCoachGeneratedModeSelectorOptionOwn ); } +FHyperTwistTrainingCoachGeneratedModeSelectorOptionOwnedExecutionSplitPhaseNormalizationSummaryPreviewSurface +AHyperTwistTrainingSessionActor::GetDisplayedCoachGeneratedModeSelectorOptionOwnedExecutionSplitPhaseNormalizationSummaryPreviewSurface( + const FString& SelectorId +) const +{ + const FHyperTwistTrainingCoachGeneratedModeSelectorOptionMenuSurface OptionMenuSurface = + GetDisplayedCoachGeneratedModeSelectorOptionMenuSurface(SelectorId); + const FHyperTwistTrainingCoachGeneratedModeSelectorOptionLaunchRequestPreviewSurface + OptionLaunchRequestPreviewSurface = + GetDisplayedCoachGeneratedModeSelectorOptionLaunchRequestPreviewSurface(SelectorId); + return UHyperTwistTrainingCoachLibrary:: + DeriveCoachGeneratedModeSelectorOptionOwnedExecutionSplitPhaseNormalizationSummaryPreviewSurface( + 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 52b47c0..f3ee42c 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingCoachLibrary.h +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingCoachLibrary.h @@ -907,4 +907,14 @@ public: const FHyperTwistTrainingCoachGeneratedModeSelectorOptionLaunchRequestPreviewSurface& OptionLaunchRequestPreviewSurface ); + + UFUNCTION(BlueprintPure, Category = "HyperTwist|Training|Coach") + static FHyperTwistTrainingCoachGeneratedModeSelectorOptionOwnedExecutionSplitPhaseNormalizationSummaryPreviewSurface + DeriveCoachGeneratedModeSelectorOptionOwnedExecutionSplitPhaseNormalizationSummaryPreviewSurface( + 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 d9d371e..1d36773 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingPanelWidget.h +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingPanelWidget.h @@ -374,6 +374,12 @@ public: const FString& SelectorId ) const; + UFUNCTION(BlueprintPure, Category = "HyperTwist|Training|Coach") + FHyperTwistTrainingCoachGeneratedModeSelectorOptionOwnedExecutionSplitPhaseNormalizationSummaryPreviewSurface + GetDisplayedCoachGeneratedModeSelectorOptionOwnedExecutionSplitPhaseNormalizationSummaryPreviewSurface( + 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 be533c8..f1b5df0 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingSessionActor.h +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingSessionActor.h @@ -355,6 +355,12 @@ public: const FString& SelectorId ) const; + UFUNCTION(BlueprintPure, Category = "HyperTwist|Training|Coach") + FHyperTwistTrainingCoachGeneratedModeSelectorOptionOwnedExecutionSplitPhaseNormalizationSummaryPreviewSurface + GetDisplayedCoachGeneratedModeSelectorOptionOwnedExecutionSplitPhaseNormalizationSummaryPreviewSurface( + 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 e27d4bd..3126445 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingTypes.h +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingTypes.h @@ -12829,3 +12829,221 @@ struct FHyperTwistTrainingCoachGeneratedModeSelectorOptionOwnedExecutionSplitPha || bHasOwnedExecutionSplitPhaseDuplicatePruningPreviewOptions; } }; + +USTRUCT(BlueprintType) +struct FHyperTwistTrainingCoachGeneratedModeSelectorOptionOwnedExecutionSplitPhaseNormalizationSummaryPreviewEntrySurface +{ + 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 RawSplitPhaseCountLine; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString SurvivorCountLine; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString DuplicatePruningLine; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString InvalidDefinitionLine; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString LabelProvenanceLine; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString FallbackPhaseIdLine; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString PreviewPostureLine; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString DetailLine; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + int32 RawSplitPhaseCount = 0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + int32 SurvivorPhaseCount = 0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + int32 DuplicatePrunedPhaseCount = 0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + int32 InvalidRawDefinitionCount = 0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + int32 ExplicitLabelPhaseCount = 0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + int32 FallbackLabelPhaseCount = 0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bMatchesCurrentSelection = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bStartsOwnedCaseImmediately = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bHasExactNormalizationSummaryPreview = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bRetainsActiveDeckSplitPhaseNormalizationSummary = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bHasFallbackLabels = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bHasDuplicatePrunedPhases = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bHasInvalidRawDefinitions = 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() + || !RawSplitPhaseCountLine.IsEmpty() + || !LabelProvenanceLine.IsEmpty() + || !PreviewPostureLine.IsEmpty() + || LaunchRequestPreviewKind + != EHyperTwistTrainingCoachGeneratedModeOptionLaunchRequestPreviewKind::None + || bCanPreview; + } +}; + +USTRUCT(BlueprintType) +struct FHyperTwistTrainingCoachGeneratedModeSelectorOptionOwnedExecutionSplitPhaseNormalizationSummaryPreviewSurface +{ + 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 NormalizationSummaryLine; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString LabelFallbackSummaryLine; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString PreviewPostureSummaryLine; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString CurrentSelectionNormalizationSummaryLine; + + 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 RawSplitPhaseDefinitionCount = 0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + int32 SurvivorPhaseCount = 0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + int32 DuplicatePrunedPhaseCount = 0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + int32 InvalidRawDefinitionCount = 0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + int32 ExplicitLabelPhaseCount = 0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + int32 FallbackLabelPhaseCount = 0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + int32 OptionsWithFallbackLabelCount = 0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + int32 OptionsWithDuplicatePruningCount = 0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + int32 OptionsWithInvalidRawDefinitionsCount = 0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bHasSelector = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bHasOwnedExecutionSplitPhaseNormalizationSummaryPreviewOptions = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bHasDirectSplitPhaseNormalizationSummaryPreviewOptions = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bHasGeneratedSplitPhaseNormalizationSummaryPreviewOptions = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bHasBlockedSplitPhaseNormalizationSummaryPreviewOptions = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bCurrentSelectionHasExactSplitPhaseNormalizationSummaryPreview = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bCurrentSelectionRetainsActiveDeckSplitPhaseNormalizationSummary = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bCurrentSelectionHasFallbackLabels = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bCurrentSelectionHasDuplicatePrunedPhases = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bCurrentSelectionHasInvalidRawDefinitions = false; + + bool IsStructurallyValid() const + { + return !StatusLine.IsEmpty() + || !SelectorId.IsEmpty() + || OptionEntries.Num() > 0 + || bHasSelector + || bHasOwnedExecutionSplitPhaseNormalizationSummaryPreviewOptions; + } +}; diff --git a/docs/HYPERTWIST_ROADMAP_OVERHAUL_EXPANSION_GUIDE.md b/docs/HYPERTWIST_ROADMAP_OVERHAUL_EXPANSION_GUIDE.md index 8fcb213..54abd28 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 duplicate-pruning / survivor provenance preview surface over the already-cached selector/runtime/request state, so gameplay/Blueprint callers can inspect which raw split-phase definitions actually survive normalization and duplicate rejection without rebuilding timing-policy normalization rules directly - - the current next bounded packet is to expose one thin generated-mode selector option owned execution split-phase normalization summary surface on those same two consumer surfaces so gameplay/Blueprint callers can inspect raw definitions, survivors, label fallback, and blocked preview posture from one surface instead of merging the existing split-phase normalization previews 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 normalization summary surface over the already-cached selector/runtime/request state, so gameplay/Blueprint callers can inspect raw definitions, survivors, label fallback, and blocked preview posture from one surface instead of merging the existing split-phase normalization previews themselves + - the current next bounded packet is to expose one thin generated-mode selector option owned execution split-phase raw-row / normalized-phase fusion preview surface on those same two consumer surfaces so gameplay/Blueprint callers can inspect duplicate-pruning survivors and normalized label-fallback phase detail from one fused row/phase surface instead of joining separate raw-row and normalized-phase arrays 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_NORMALIZATION_SUMMARY_PREVIEW_SURFACE_PACKET_2026-05-08.md b/docs/arch/HYPERTWIST_PHASE4_GAMEPLAY_GENERATED_MODE_SELECTOR_OPTION_OWNED_EXECUTION_SPLIT_PHASE_NORMALIZATION_SUMMARY_PREVIEW_SURFACE_PACKET_2026-05-08.md new file mode 100644 index 0000000..dbbf2a4 --- /dev/null +++ b/docs/arch/HYPERTWIST_PHASE4_GAMEPLAY_GENERATED_MODE_SELECTOR_OPTION_OWNED_EXECUTION_SPLIT_PHASE_NORMALIZATION_SUMMARY_PREVIEW_SURFACE_PACKET_2026-05-08.md @@ -0,0 +1,89 @@ +# HyperTwist Phase 4 gameplay generated-mode selector option owned execution split-phase normalization summary 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 split-phase duplicate-pruning / survivor provenance preview surface by exposing one parameterized owned execution split-phase normalization summary preview surface for a chosen selector id. + +The open tasks were: + +- stop forcing gameplay / Blueprint callers to merge separate split-phase detail, label-fallback provenance, and duplicate-pruning preview surfaces just to render one normalization summary +- expose direct covered-case normalization summary and generated owned-execution normalization summary on the same thin consumer lane +- expose blocked preview posture from that same fused summary layer 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 split-phase normalization summary preview surfaces in shared training types +- add a coach-library helper that composes the already-landed split-phase label-fallback and duplicate-pruning provenance surfaces into one thin summary layer +- expose that parameterized split-phase normalization summary 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 normalized split-phase detail, explicit-vs-fallback label provenance, and raw-definition duplicate-pruning / survivor provenance for imported selector options +- callers could already inspect each layer separately for a chosen option + +But one thin consumer seam was still open: + +- callers still had to merge multiple split-phase normalization preview surfaces themselves just to render one summary of raw definitions, survivors, fallback labels, and blocked posture + +That meant: + +- the underlying preview layers were already thin and reusable +- but a common gameplay-facing normalization summary still leaked cross-surface merge work into callers + +So the next bounded move was: + +- keep the already-landed split-phase detail, label-fallback provenance, and duplicate-pruning provenance surfaces unchanged in behavior +- add one narrow summary surface above them that simply composes those existing layers into a caller-ready normalization summary + +## What landed + +Primary code changes: + +- `UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingTypes.h` + - added `FHyperTwistTrainingCoachGeneratedModeSelectorOptionOwnedExecutionSplitPhaseNormalizationSummaryPreviewEntrySurface` + - added `FHyperTwistTrainingCoachGeneratedModeSelectorOptionOwnedExecutionSplitPhaseNormalizationSummaryPreviewSurface` +- `UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingCoachLibrary.h` +- `UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingCoachLibrary.cpp` + - added `DeriveCoachGeneratedModeSelectorOptionOwnedExecutionSplitPhaseNormalizationSummaryPreviewSurface(...)` + - the normalization summary preview is composed from the already-landed split-phase label-fallback and duplicate-pruning provenance surfaces instead of rebuilding normalization behavior +- `UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingPanelWidget.h` +- `UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingPanelWidget.cpp` + - added `GetDisplayedCoachGeneratedModeSelectorOptionOwnedExecutionSplitPhaseNormalizationSummaryPreviewSurface(const FString& SelectorId)` +- `UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingSessionActor.h` +- `UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingSessionActor.cpp` + - added `GetDisplayedCoachGeneratedModeSelectorOptionOwnedExecutionSplitPhaseNormalizationSummaryPreviewSurface(const FString& SelectorId)` + +Consumer result: + +- gameplay / Blueprint callers can now inspect raw split-phase definition counts, survivor counts, duplicate-pruned rows, invalid rows, explicit-vs-fallback label provenance, and blocked preview posture for a chosen selector option from one thin surface instead of merging the existing split-phase normalization previews themselves + +## Validation + +- full `Development Editor|Win64` solution build for `C:\HyperTwist\UnrealHyperTwist\UnrealHyperTwist.sln` +- result: `0` warnings, `0` errors