diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingCoachLibrary.cpp b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingCoachLibrary.cpp index 3681164..cdb4983 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingCoachLibrary.cpp +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingCoachLibrary.cpp @@ -9533,6 +9533,621 @@ UHyperTwistTrainingCoachLibrary:: return Surface; } +FHyperTwistTrainingCoachGeneratedModeSelectorOptionOwnedExecutionSplitPhaseDuplicatePruningSurvivorProvenancePreviewSurface +UHyperTwistTrainingCoachLibrary:: + DeriveCoachGeneratedModeSelectorOptionOwnedExecutionSplitPhaseDuplicatePruningSurvivorProvenancePreviewSurface( + const FHyperTwistTrainingRunState& RunState, + const FString& SelectorId, + const FHyperTwistTrainingCoachGeneratedModeSelectorOptionMenuSurface& OptionMenuSurface, + const FHyperTwistTrainingCoachGeneratedModeSelectorOptionLaunchRequestPreviewSurface& + OptionLaunchRequestPreviewSurface + ) +{ + const FHyperTwistTrainingCoachGeneratedModeSelectorOptionOwnedExecutionSplitPhasePhaseIdLabelFallbackProvenancePreviewSurface + SplitPhaseLabelProvenancePreviewSurface = + DeriveCoachGeneratedModeSelectorOptionOwnedExecutionSplitPhasePhaseIdLabelFallbackProvenancePreviewSurface( + RunState, + SelectorId, + OptionMenuSurface, + OptionLaunchRequestPreviewSurface + ); + + FHyperTwistTrainingCoachGeneratedModeSelectorOptionOwnedExecutionSplitPhaseDuplicatePruningSurvivorProvenancePreviewSurface + Surface; + Surface.SelectorId = SelectorId; + Surface.SelectorLabel = OptionMenuSurface.SelectorLabel; + Surface.BindingLabel = OptionMenuSurface.BindingLabel; + Surface.Headline = + TEXT("Generated Mode Selector Option Owned-Execution Split-Phase Duplicate-Pruning/Survivor Provenance Preview"); + Surface.bHasSelector = OptionMenuSurface.bHasSelector; + + if (!SelectorId.IsEmpty()) + { + Surface.Headline = OptionMenuSurface.SelectorLabel.IsEmpty() + ? FString::Printf( + TEXT("Generated Mode Selector Option Owned-Execution Split-Phase Duplicate-Pruning/Survivor Provenance Preview: %s"), + *SelectorId + ) + : FString::Printf( + TEXT("Generated Mode Selector Option Owned-Execution Split-Phase Duplicate-Pruning/Survivor Provenance Preview: %s"), + *OptionMenuSurface.SelectorLabel + ); + } + + if (SelectorId.IsEmpty()) + { + Surface.StatusLine = + TEXT("Generated-mode selector option owned-execution split-phase duplicate-pruning / survivor provenance preview requires a selector id from the active selector roster."); + Surface.RawSplitPhaseCountSummaryLine = TEXT("Split-phase duplicate-pruning: n/a"); + Surface.DuplicatePruningSummaryLine = TEXT("Survivor provenance detail: n/a"); + Surface.CurrentSelectionDuplicatePruningLine = TEXT("Current selection: n/a"); + Surface.DetailLine = SplitPhaseLabelProvenancePreviewSurface.DetailLine.IsEmpty() + ? TEXT("Launch-request preview: n/a") + : SplitPhaseLabelProvenancePreviewSurface.DetailLine; + return Surface; + } + + const FHyperTwistTrainingImportedRuntimeSurface& RuntimeSurface = + RunState.ActiveDeck.ImportedRuntimeSurface; + const bool bHasGeneratedModeSurface = RuntimeSurface.IsStructurallyValid() + && RuntimeSurface.SurfaceKind.Equals(TEXT("generated-mode"), ESearchCase::IgnoreCase); + + if (!RunState.IsStructurallyValid() || !bHasGeneratedModeSurface) + { + Surface.StatusLine = SplitPhaseLabelProvenancePreviewSurface.StatusLine; + Surface.RawSplitPhaseCountSummaryLine = TEXT("Split-phase duplicate-pruning: n/a"); + Surface.DuplicatePruningSummaryLine = TEXT("Survivor provenance detail: n/a"); + Surface.CurrentSelectionDuplicatePruningLine = + OptionMenuSurface.bHasUnresolvedSelectionDetail + ? OptionMenuSurface.UnresolvedSelectionLine + : (OptionMenuSurface.bHasSelection + ? TEXT("Current selection remains cached, but no active owned-execution split-phase duplicate-pruning preview is available.") + : TEXT("Current selection: unresolved")); + Surface.DetailLine = SplitPhaseLabelProvenancePreviewSurface.DetailLine; + return Surface; + } + + const FHyperTwistTrainingTimingPolicy RawTimingPolicy = RunState.ActiveDeck.TimingPolicy; + const FHyperTwistTrainingTimingPolicy PreviewTimingPolicy = + UHyperTwistTrainingLibrary::NormalizeTimingPolicy(RawTimingPolicy); + + const auto BuildInvalidReason = []( + const FHyperTwistTrainingSplitPhaseDefinition& PhaseDefinition + ) -> FString + { + TArray Reasons; + if (PhaseDefinition.PhaseId.IsEmpty()) + { + Reasons.Add(TEXT("phase id is empty")); + } + if (PhaseDefinition.Order < 0) + { + Reasons.Add(TEXT("order is negative")); + } + + return Reasons.Num() > 0 + ? FString::Join(Reasons, TEXT("; ")) + : FString(TEXT("definition is structurally invalid")); + }; + + const auto BuildRawPhaseEntries = [&BuildInvalidReason]( + const FHyperTwistTrainingTimingPolicy& SourceTimingPolicy, + const FHyperTwistTrainingTimingPolicy& NormalizedTimingPolicy, + TArray& + OutEntries, + int32& OutSurvivorPhaseCount, + int32& OutDuplicatePrunedPhaseCount, + int32& OutInvalidRawDefinitionCount + ) + { + struct FSurvivorMetadata + { + int32 NormalizedOrder = -1; + int32 EvaluationOrder = -1; + FString NormalizedLabel; + }; + + OutEntries.Reset(); + OutSurvivorPhaseCount = 0; + OutDuplicatePrunedPhaseCount = 0; + OutInvalidRawDefinitionCount = 0; + + TArray SortedPhases = + SourceTimingPolicy.SplitPhases; + SortedPhases.Sort([]( + const FHyperTwistTrainingSplitPhaseDefinition& Left, + const FHyperTwistTrainingSplitPhaseDefinition& Right + ) + { + if (Left.Order != Right.Order) + { + return Left.Order < Right.Order; + } + + return Left.PhaseId < Right.PhaseId; + }); + + TMap SurvivorMetadataByPhaseId; + int32 NextNormalizedOrder = 0; + for (int32 SortedIndex = 0; SortedIndex < SortedPhases.Num(); ++SortedIndex) + { + const FHyperTwistTrainingSplitPhaseDefinition& PhaseDefinition = + SortedPhases[SortedIndex]; + const FString DisplayPhaseId = PhaseDefinition.PhaseId.IsEmpty() + ? TEXT("") + : PhaseDefinition.PhaseId; + const FString DisplayLabel = PhaseDefinition.Label.IsEmpty() + ? TEXT("") + : PhaseDefinition.Label; + + FHyperTwistTrainingCoachGeneratedModeSelectorOptionOwnedExecutionSplitPhaseDuplicatePruningSurvivorProvenancePreviewRawPhaseEntrySurface + Entry; + Entry.PhaseId = PhaseDefinition.PhaseId; + Entry.SourceLabel = PhaseDefinition.Label; + Entry.RawOrder = PhaseDefinition.Order; + Entry.EvaluationOrder = SortedIndex; + Entry.bSourceLabelWasExplicit = !PhaseDefinition.Label.IsEmpty(); + Entry.bStructurallyValid = PhaseDefinition.IsStructurallyValid(); + Entry.RawDefinitionLine = FString::Printf( + TEXT("Raw definition before normalize: order %d | phase id %s | label %s"), + PhaseDefinition.Order, + *DisplayPhaseId, + *DisplayLabel + ); + Entry.EvaluationOrderLine = FString::Printf( + TEXT("Normalization evaluation order on execute: #%d"), + SortedIndex + ); + + if (!Entry.bStructurallyValid) + { + Entry.bDroppedBecauseInvalid = true; + Entry.NormalizationOutcomeLine = FString::Printf( + TEXT("Normalization outcome on execute: skipped because the raw definition is structurally invalid (%s)"), + *BuildInvalidReason(PhaseDefinition) + ); + Entry.DuplicatePruningProvenanceLine = + TEXT("Duplicate-pruning provenance on execute: invalid raw definitions never enter duplicate resolution."); + Entry.DetailLine = FString::Printf( + TEXT("%s | %s | %s"), + *Entry.RawDefinitionLine, + *Entry.NormalizationOutcomeLine, + *Entry.DuplicatePruningProvenanceLine + ); + Entry.bCanPreview = true; + ++OutInvalidRawDefinitionCount; + OutEntries.Add(MoveTemp(Entry)); + continue; + } + + if (const FSurvivorMetadata* ExistingSurvivor = + SurvivorMetadataByPhaseId.Find(PhaseDefinition.PhaseId)) + { + Entry.bDroppedBecauseDuplicate = true; + Entry.NormalizedOrder = ExistingSurvivor->NormalizedOrder; + Entry.SurvivorNormalizedLabel = ExistingSurvivor->NormalizedLabel; + Entry.NormalizationOutcomeLine = FString::Printf( + TEXT("Normalization outcome on execute: dropped as a duplicate raw definition for phase id %s."), + *DisplayPhaseId + ); + Entry.DuplicatePruningProvenanceLine = FString::Printf( + TEXT("Duplicate-pruning provenance on execute: earlier survivor already retained phase id %s at normalized order #%d with label %s."), + *DisplayPhaseId, + ExistingSurvivor->NormalizedOrder, + *ExistingSurvivor->NormalizedLabel + ); + Entry.DetailLine = FString::Printf( + TEXT("%s | %s | %s"), + *Entry.RawDefinitionLine, + *Entry.NormalizationOutcomeLine, + *Entry.DuplicatePruningProvenanceLine + ); + Entry.bCanPreview = true; + ++OutDuplicatePrunedPhaseCount; + OutEntries.Add(MoveTemp(Entry)); + continue; + } + + Entry.bSurvivesNormalization = true; + Entry.NormalizedOrder = NextNormalizedOrder; + Entry.SurvivorNormalizedLabel = + NormalizedTimingPolicy.SplitPhases.IsValidIndex(NextNormalizedOrder) + && NormalizedTimingPolicy.SplitPhases[NextNormalizedOrder].PhaseId + == PhaseDefinition.PhaseId + ? NormalizedTimingPolicy.SplitPhases[NextNormalizedOrder].Label + : (PhaseDefinition.Label.IsEmpty() + ? PhaseDefinition.PhaseId + : PhaseDefinition.Label); + Entry.NormalizationOutcomeLine = FString::Printf( + TEXT("Normalization outcome on execute: retained as survivor #%d with normalized label %s (%s)."), + Entry.NormalizedOrder, + *Entry.SurvivorNormalizedLabel, + *DisplayPhaseId + ); + Entry.DuplicatePruningProvenanceLine = FString::Printf( + TEXT("Duplicate-pruning provenance on execute: first valid definition for phase id %s survives duplicate rejection."), + *DisplayPhaseId + ); + Entry.DetailLine = FString::Printf( + TEXT("%s | %s | %s"), + *Entry.RawDefinitionLine, + *Entry.NormalizationOutcomeLine, + *Entry.DuplicatePruningProvenanceLine + ); + Entry.bCanPreview = true; + + FSurvivorMetadata SurvivorMetadata; + SurvivorMetadata.NormalizedOrder = Entry.NormalizedOrder; + SurvivorMetadata.EvaluationOrder = Entry.EvaluationOrder; + SurvivorMetadata.NormalizedLabel = Entry.SurvivorNormalizedLabel; + SurvivorMetadataByPhaseId.Add(PhaseDefinition.PhaseId, MoveTemp(SurvivorMetadata)); + + ++NextNormalizedOrder; + ++OutSurvivorPhaseCount; + OutEntries.Add(MoveTemp(Entry)); + } + }; + + const auto BuildRawSplitPhaseCountLine = []( + const int32 RawSplitPhaseCount + ) -> FString + { + return RawSplitPhaseCount > 0 + ? FString::Printf( + TEXT("Raw split-phase definitions before normalize: %d"), + RawSplitPhaseCount + ) + : FString(TEXT("Raw split-phase definitions before normalize: none")); + }; + + const auto BuildSurvivorCountLine = []( + const int32 SurvivorPhaseCount + ) -> FString + { + return SurvivorPhaseCount > 0 + ? FString::Printf( + TEXT("Surviving normalized split phases on execute: %d"), + SurvivorPhaseCount + ) + : FString(TEXT("Surviving normalized split phases on execute: none")); + }; + + const auto BuildDuplicatePruningLine = []( + const int32 DuplicatePrunedPhaseCount + ) -> FString + { + return DuplicatePrunedPhaseCount > 0 + ? FString::Printf( + TEXT("Duplicate-pruned raw split-phase definitions on execute: %d"), + DuplicatePrunedPhaseCount + ) + : FString(TEXT("Duplicate-pruned raw split-phase definitions on execute: none")); + }; + + const auto BuildInvalidDefinitionLine = []( + const int32 InvalidRawDefinitionCount + ) -> FString + { + return InvalidRawDefinitionCount > 0 + ? FString::Printf( + TEXT("Invalid raw split-phase definitions skipped on execute: %d"), + InvalidRawDefinitionCount + ) + : FString(TEXT("Invalid raw split-phase definitions skipped on execute: none")); + }; + + const auto BuildSurvivorPhaseIdLine = []( + const TArray& + RawPhaseEntries + ) -> FString + { + TArray SurvivorPhaseIds; + for (const FHyperTwistTrainingCoachGeneratedModeSelectorOptionOwnedExecutionSplitPhaseDuplicatePruningSurvivorProvenancePreviewRawPhaseEntrySurface& + Entry : RawPhaseEntries) + { + if (Entry.bSurvivesNormalization) + { + SurvivorPhaseIds.Add(Entry.PhaseId); + } + } + + return SurvivorPhaseIds.Num() > 0 + ? FString::Printf( + TEXT("Surviving phase ids on execute: %s"), + *FString::Join(SurvivorPhaseIds, TEXT(", ")) + ) + : FString(TEXT("Surviving phase ids on execute: none")); + }; + + TArray + TemplateRawPhaseEntries; + int32 TemplateSurvivorPhaseCount = 0; + int32 TemplateDuplicatePrunedPhaseCount = 0; + int32 TemplateInvalidRawDefinitionCount = 0; + BuildRawPhaseEntries( + RawTimingPolicy, + PreviewTimingPolicy, + TemplateRawPhaseEntries, + TemplateSurvivorPhaseCount, + TemplateDuplicatePrunedPhaseCount, + TemplateInvalidRawDefinitionCount + ); + const int32 TemplateRawSplitPhaseCount = TemplateRawPhaseEntries.Num(); + const FString TemplateRawSplitPhaseCountLine = BuildRawSplitPhaseCountLine( + TemplateRawSplitPhaseCount + ); + const FString TemplateSurvivorCountLine = BuildSurvivorCountLine( + TemplateSurvivorPhaseCount + ); + const FString TemplateDuplicatePruningLine = BuildDuplicatePruningLine( + TemplateDuplicatePrunedPhaseCount + ); + const FString TemplateInvalidDefinitionLine = BuildInvalidDefinitionLine( + TemplateInvalidRawDefinitionCount + ); + const FString TemplateSurvivorPhaseIdLine = BuildSurvivorPhaseIdLine( + TemplateRawPhaseEntries + ); + + for (const FHyperTwistTrainingCoachGeneratedModeSelectorOptionOwnedExecutionSplitPhasePhaseIdLabelFallbackProvenancePreviewEntrySurface& + SplitPhaseEntry : SplitPhaseLabelProvenancePreviewSurface.OptionEntries) + { + FHyperTwistTrainingCoachGeneratedModeSelectorOptionOwnedExecutionSplitPhaseDuplicatePruningSurvivorProvenancePreviewEntrySurface + Entry; + Entry.OptionId = SplitPhaseEntry.OptionId; + Entry.Label = SplitPhaseEntry.Label; + Entry.OptionValue = SplitPhaseEntry.OptionValue; + Entry.LaunchRequestPreviewKind = SplitPhaseEntry.LaunchRequestPreviewKind; + Entry.bMatchesCurrentSelection = SplitPhaseEntry.bMatchesCurrentSelection; + Entry.bStartsOwnedCaseImmediately = SplitPhaseEntry.bStartsOwnedCaseImmediately; + + switch (SplitPhaseEntry.LaunchRequestPreviewKind) + { + case EHyperTwistTrainingCoachGeneratedModeOptionLaunchRequestPreviewKind:: + DirectOwnedCaseStart: + Entry.RawPhaseEntries = TemplateRawPhaseEntries; + Entry.RawSplitPhaseCount = TemplateRawSplitPhaseCount; + Entry.SurvivorPhaseCount = TemplateSurvivorPhaseCount; + Entry.DuplicatePrunedPhaseCount = TemplateDuplicatePrunedPhaseCount; + Entry.InvalidRawDefinitionCount = TemplateInvalidRawDefinitionCount; + Entry.bHasDuplicatePrunedPhases = TemplateDuplicatePrunedPhaseCount > 0; + Entry.bHasInvalidRawDefinitions = TemplateInvalidRawDefinitionCount > 0; + Entry.RawSplitPhaseCountLine = TemplateRawSplitPhaseCountLine; + Entry.SurvivorCountLine = TemplateSurvivorCountLine; + Entry.DuplicatePruningLine = TemplateDuplicatePruningLine; + Entry.InvalidDefinitionLine = TemplateInvalidDefinitionLine; + Entry.SurvivorPhaseIdLine = TemplateSurvivorPhaseIdLine; + Entry.SplitPhaseSourceLine = + TEXT("Split-phase duplicate-pruning provenance source: exact active-deck raw split-phase set normalized and duplicate-pruned for direct covered-case start."); + Entry.PreviewStabilityLine = SplitPhaseEntry.PreviewStabilityLine; + Entry.bHasExactSplitPhaseDuplicatePruningPreview = true; + Entry.bRetainsActiveDeckSplitPhaseDuplicatePruningProvenance = true; + Entry.bCanPreview = true; + ++Surface.DirectExactOptionCount; + if (Entry.bHasDuplicatePrunedPhases) + { + ++Surface.OptionsWithDuplicatePruningCount; + } + if (Entry.bHasInvalidRawDefinitions) + { + ++Surface.OptionsWithInvalidRawDefinitionsCount; + } + Surface.RawSplitPhaseDefinitionCount += Entry.RawSplitPhaseCount; + Surface.SurvivorPhaseCount += Entry.SurvivorPhaseCount; + Surface.DuplicatePrunedPhaseCount += Entry.DuplicatePrunedPhaseCount; + Surface.InvalidRawDefinitionCount += Entry.InvalidRawDefinitionCount; + break; + + case EHyperTwistTrainingCoachGeneratedModeOptionLaunchRequestPreviewKind:: + StoredRequestReusable: + case EHyperTwistTrainingCoachGeneratedModeOptionLaunchRequestPreviewKind:: + RefreshRequired: + Entry.RawPhaseEntries = TemplateRawPhaseEntries; + Entry.RawSplitPhaseCount = TemplateRawSplitPhaseCount; + Entry.SurvivorPhaseCount = TemplateSurvivorPhaseCount; + Entry.DuplicatePrunedPhaseCount = TemplateDuplicatePrunedPhaseCount; + Entry.InvalidRawDefinitionCount = TemplateInvalidRawDefinitionCount; + Entry.bHasDuplicatePrunedPhases = TemplateDuplicatePrunedPhaseCount > 0; + Entry.bHasInvalidRawDefinitions = TemplateInvalidRawDefinitionCount > 0; + Entry.RawSplitPhaseCountLine = TemplateRawSplitPhaseCountLine; + Entry.SurvivorCountLine = TemplateSurvivorCountLine; + Entry.DuplicatePruningLine = TemplateDuplicatePruningLine; + Entry.InvalidDefinitionLine = TemplateInvalidDefinitionLine; + Entry.SurvivorPhaseIdLine = TemplateSurvivorPhaseIdLine; + Entry.SplitPhaseSourceLine = + TEXT("Split-phase duplicate-pruning provenance source: exact source-deck raw split-phase set normalized and duplicate-pruned for generated owned execution."); + Entry.PreviewStabilityLine = SplitPhaseEntry.PreviewStabilityLine; + Entry.bHasExactSplitPhaseDuplicatePruningPreview = true; + Entry.bRetainsActiveDeckSplitPhaseDuplicatePruningProvenance = true; + Entry.bCanPreview = true; + ++Surface.GeneratedExactOptionCount; + if (Entry.bHasDuplicatePrunedPhases) + { + ++Surface.OptionsWithDuplicatePruningCount; + } + if (Entry.bHasInvalidRawDefinitions) + { + ++Surface.OptionsWithInvalidRawDefinitionsCount; + } + Surface.RawSplitPhaseDefinitionCount += Entry.RawSplitPhaseCount; + Surface.SurvivorPhaseCount += Entry.SurvivorPhaseCount; + Surface.DuplicatePrunedPhaseCount += Entry.DuplicatePrunedPhaseCount; + Surface.InvalidRawDefinitionCount += Entry.InvalidRawDefinitionCount; + break; + + case EHyperTwistTrainingCoachGeneratedModeOptionLaunchRequestPreviewKind:: + UnresolvedAfterChoice: + Entry.RawSplitPhaseCountLine = + TEXT("Raw split-phase definitions before normalize: blocked until every required selector is resolved."); + Entry.SurvivorCountLine = + TEXT("Surviving normalized split phases on execute: blocked until every required selector is resolved."); + Entry.DuplicatePruningLine = + TEXT("Duplicate-pruned raw split-phase definitions on execute: blocked until every required selector is resolved."); + Entry.InvalidDefinitionLine = + TEXT("Invalid raw split-phase definitions skipped on execute: blocked until every required selector is resolved."); + Entry.SurvivorPhaseIdLine = + TEXT("Surviving phase ids on execute: blocked until every required selector is resolved."); + Entry.SplitPhaseSourceLine = + TEXT("Split-phase duplicate-pruning provenance source: blocked while selector coverage remains incomplete."); + Entry.PreviewStabilityLine = SplitPhaseEntry.PreviewStabilityLine; + Entry.bPreviewBlocked = true; + Entry.bCanPreview = true; + ++Surface.BlockedOptionCount; + break; + + case EHyperTwistTrainingCoachGeneratedModeOptionLaunchRequestPreviewKind:: + PayloadUnavailable: + Entry.RawSplitPhaseCountLine = + TEXT("Raw split-phase definitions before normalize: blocked until a structurally valid launch payload can be derived."); + Entry.SurvivorCountLine = + TEXT("Surviving normalized split phases on execute: blocked until a structurally valid launch payload can be derived."); + Entry.DuplicatePruningLine = + TEXT("Duplicate-pruned raw split-phase definitions on execute: blocked until a structurally valid launch payload can be derived."); + Entry.InvalidDefinitionLine = + TEXT("Invalid raw split-phase definitions skipped on execute: blocked until a structurally valid launch payload can be derived."); + Entry.SurvivorPhaseIdLine = + TEXT("Surviving phase ids on execute: blocked until a structurally valid launch payload can be derived."); + Entry.SplitPhaseSourceLine = + TEXT("Split-phase duplicate-pruning provenance source: blocked while the hypothetical generated-mode payload remains structurally invalid."); + Entry.PreviewStabilityLine = SplitPhaseEntry.PreviewStabilityLine; + Entry.bPreviewBlocked = true; + Entry.bCanPreview = true; + ++Surface.BlockedOptionCount; + break; + + case EHyperTwistTrainingCoachGeneratedModeOptionLaunchRequestPreviewKind::Unavailable: + case EHyperTwistTrainingCoachGeneratedModeOptionLaunchRequestPreviewKind::None: + default: + Entry.RawSplitPhaseCountLine = + TEXT("Raw split-phase definitions before normalize: unavailable"); + Entry.SurvivorCountLine = + TEXT("Surviving normalized split phases on execute: unavailable"); + Entry.DuplicatePruningLine = + TEXT("Duplicate-pruned raw split-phase definitions on execute: unavailable"); + Entry.InvalidDefinitionLine = + TEXT("Invalid raw split-phase definitions skipped on execute: unavailable"); + Entry.SurvivorPhaseIdLine = + TEXT("Surviving phase ids on execute: unavailable"); + Entry.SplitPhaseSourceLine = + TEXT("Split-phase duplicate-pruning provenance source: unavailable"); + Entry.PreviewStabilityLine = SplitPhaseEntry.PreviewStabilityLine; + Entry.bCanPreview = false; + ++Surface.UnavailableOptionCount; + break; + } + + Entry.DetailLine = Entry.SplitPhaseSourceLine.IsEmpty() + ? Entry.PreviewStabilityLine + : FString::Printf( + TEXT("%s | %s"), + *Entry.PreviewStabilityLine, + *Entry.SplitPhaseSourceLine + ); + + if (Entry.bMatchesCurrentSelection) + { + Surface.bCurrentSelectionHasExactSplitPhaseDuplicatePruningPreview = + Entry.bHasExactSplitPhaseDuplicatePruningPreview; + Surface.bCurrentSelectionRetainsActiveDeckSplitPhaseDuplicatePruningProvenance = + Entry.bRetainsActiveDeckSplitPhaseDuplicatePruningProvenance; + Surface.bCurrentSelectionHasDuplicatePrunedPhases = + Entry.bHasDuplicatePrunedPhases; + Surface.bCurrentSelectionHasInvalidRawDefinitions = + Entry.bHasInvalidRawDefinitions; + Surface.CurrentSelectionDuplicatePruningLine = FString::Printf( + TEXT("%s | %s | %s | %s | %s"), + *Entry.RawSplitPhaseCountLine, + *Entry.SurvivorCountLine, + *Entry.DuplicatePruningLine, + *Entry.InvalidDefinitionLine, + *Entry.SurvivorPhaseIdLine + ); + } + + Surface.OptionEntries.Add(MoveTemp(Entry)); + } + + Surface.bHasOwnedExecutionSplitPhaseDuplicatePruningPreviewOptions = + Surface.OptionEntries.Num() > 0; + Surface.bHasDirectSplitPhaseDuplicatePruningPreviewOptions = + Surface.DirectExactOptionCount > 0; + Surface.bHasGeneratedSplitPhaseDuplicatePruningPreviewOptions = + Surface.GeneratedExactOptionCount > 0; + Surface.bHasBlockedSplitPhaseDuplicatePruningPreviewOptions = + Surface.BlockedOptionCount > 0; + + Surface.RawSplitPhaseCountSummaryLine = Surface.OptionEntries.Num() > 0 + ? FString::Printf( + TEXT("Split-phase duplicate-pruning: %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 duplicate-pruning: no imported options"); + Surface.DuplicatePruningSummaryLine = Surface.OptionEntries.Num() > 0 + ? FString::Printf( + TEXT("Survivor provenance detail: %d options with duplicate-pruned rows | %d options with invalid rows"), + Surface.OptionsWithDuplicatePruningCount, + Surface.OptionsWithInvalidRawDefinitionsCount + ) + : TEXT("Survivor provenance detail: no imported options"); + + if (Surface.CurrentSelectionDuplicatePruningLine.IsEmpty()) + { + if (OptionMenuSurface.bHasUnresolvedSelectionDetail) + { + Surface.CurrentSelectionDuplicatePruningLine = + OptionMenuSurface.UnresolvedSelectionLine; + } + else if (OptionMenuSurface.bHasSelection) + { + Surface.CurrentSelectionDuplicatePruningLine = + TEXT("Current selection has no additional owned-execution split-phase duplicate-pruning preview."); + } + else if (OptionMenuSurface.bHasSelector) + { + Surface.CurrentSelectionDuplicatePruningLine = + TEXT("Current selection: unresolved"); + } + else + { + Surface.CurrentSelectionDuplicatePruningLine = + 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 duplicate-pruning / survivor provenance 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 duplicate-pruning / survivor provenance preview is active, but this selector does not expose imported options."); + } + else + { + Surface.StatusLine = SplitPhaseLabelProvenancePreviewSurface.StatusLine; + } + + Surface.DetailLine = SplitPhaseLabelProvenancePreviewSurface.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 d062f29..ab5367b 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingPanelWidget.cpp +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingPanelWidget.cpp @@ -1100,6 +1100,25 @@ UHyperTwistTrainingPanelWidget::GetDisplayedCoachGeneratedModeSelectorOptionOwne ); } +FHyperTwistTrainingCoachGeneratedModeSelectorOptionOwnedExecutionSplitPhaseDuplicatePruningSurvivorProvenancePreviewSurface +UHyperTwistTrainingPanelWidget::GetDisplayedCoachGeneratedModeSelectorOptionOwnedExecutionSplitPhaseDuplicatePruningSurvivorProvenancePreviewSurface( + const FString& SelectorId +) const +{ + const FHyperTwistTrainingCoachGeneratedModeSelectorOptionMenuSurface OptionMenuSurface = + GetDisplayedCoachGeneratedModeSelectorOptionMenuSurface(SelectorId); + const FHyperTwistTrainingCoachGeneratedModeSelectorOptionLaunchRequestPreviewSurface + OptionLaunchRequestPreviewSurface = + GetDisplayedCoachGeneratedModeSelectorOptionLaunchRequestPreviewSurface(SelectorId); + return UHyperTwistTrainingCoachLibrary:: + DeriveCoachGeneratedModeSelectorOptionOwnedExecutionSplitPhaseDuplicatePruningSurvivorProvenancePreviewSurface( + 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 e91c9fb..cb6ea1b 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingSessionActor.cpp +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingSessionActor.cpp @@ -1081,6 +1081,25 @@ AHyperTwistTrainingSessionActor::GetDisplayedCoachGeneratedModeSelectorOptionOwn ); } +FHyperTwistTrainingCoachGeneratedModeSelectorOptionOwnedExecutionSplitPhaseDuplicatePruningSurvivorProvenancePreviewSurface +AHyperTwistTrainingSessionActor::GetDisplayedCoachGeneratedModeSelectorOptionOwnedExecutionSplitPhaseDuplicatePruningSurvivorProvenancePreviewSurface( + const FString& SelectorId +) const +{ + const FHyperTwistTrainingCoachGeneratedModeSelectorOptionMenuSurface OptionMenuSurface = + GetDisplayedCoachGeneratedModeSelectorOptionMenuSurface(SelectorId); + const FHyperTwistTrainingCoachGeneratedModeSelectorOptionLaunchRequestPreviewSurface + OptionLaunchRequestPreviewSurface = + GetDisplayedCoachGeneratedModeSelectorOptionLaunchRequestPreviewSurface(SelectorId); + return UHyperTwistTrainingCoachLibrary:: + DeriveCoachGeneratedModeSelectorOptionOwnedExecutionSplitPhaseDuplicatePruningSurvivorProvenancePreviewSurface( + 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 e6178c2..52b47c0 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingCoachLibrary.h +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingCoachLibrary.h @@ -897,4 +897,14 @@ public: const FHyperTwistTrainingCoachGeneratedModeSelectorOptionLaunchRequestPreviewSurface& OptionLaunchRequestPreviewSurface ); + + UFUNCTION(BlueprintPure, Category = "HyperTwist|Training|Coach") + static FHyperTwistTrainingCoachGeneratedModeSelectorOptionOwnedExecutionSplitPhaseDuplicatePruningSurvivorProvenancePreviewSurface + DeriveCoachGeneratedModeSelectorOptionOwnedExecutionSplitPhaseDuplicatePruningSurvivorProvenancePreviewSurface( + 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 d8f72d3..d9d371e 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingPanelWidget.h +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingPanelWidget.h @@ -368,6 +368,12 @@ public: const FString& SelectorId ) const; + UFUNCTION(BlueprintPure, Category = "HyperTwist|Training|Coach") + FHyperTwistTrainingCoachGeneratedModeSelectorOptionOwnedExecutionSplitPhaseDuplicatePruningSurvivorProvenancePreviewSurface + GetDisplayedCoachGeneratedModeSelectorOptionOwnedExecutionSplitPhaseDuplicatePruningSurvivorProvenancePreviewSurface( + 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 e0ea2c8..be533c8 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingSessionActor.h +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingSessionActor.h @@ -349,6 +349,12 @@ public: const FString& SelectorId ) const; + UFUNCTION(BlueprintPure, Category = "HyperTwist|Training|Coach") + FHyperTwistTrainingCoachGeneratedModeSelectorOptionOwnedExecutionSplitPhaseDuplicatePruningSurvivorProvenancePreviewSurface + GetDisplayedCoachGeneratedModeSelectorOptionOwnedExecutionSplitPhaseDuplicatePruningSurvivorProvenancePreviewSurface( + 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 69589c6..e27d4bd 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingTypes.h +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingTypes.h @@ -12565,3 +12565,267 @@ struct FHyperTwistTrainingCoachGeneratedModeSelectorOptionOwnedExecutionSplitPha || bHasOwnedExecutionSplitPhaseLabelProvenancePreviewOptions; } }; + +USTRUCT(BlueprintType) +struct FHyperTwistTrainingCoachGeneratedModeSelectorOptionOwnedExecutionSplitPhaseDuplicatePruningSurvivorProvenancePreviewRawPhaseEntrySurface +{ + GENERATED_BODY() + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString PhaseId; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString SourceLabel; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + int32 RawOrder = 0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + int32 EvaluationOrder = -1; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + int32 NormalizedOrder = -1; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString SurvivorNormalizedLabel; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString RawDefinitionLine; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString EvaluationOrderLine; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString NormalizationOutcomeLine; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString DuplicatePruningProvenanceLine; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString DetailLine; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bSourceLabelWasExplicit = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bStructurallyValid = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bSurvivesNormalization = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bDroppedBecauseDuplicate = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bDroppedBecauseInvalid = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bCanPreview = false; + + bool IsStructurallyValid() const + { + return !PhaseId.IsEmpty() + || !RawDefinitionLine.IsEmpty() + || !NormalizationOutcomeLine.IsEmpty() + || !DuplicatePruningProvenanceLine.IsEmpty() + || bCanPreview; + } +}; + +USTRUCT(BlueprintType) +struct FHyperTwistTrainingCoachGeneratedModeSelectorOptionOwnedExecutionSplitPhaseDuplicatePruningSurvivorProvenancePreviewEntrySurface +{ + 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 SurvivorPhaseIdLine; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString SplitPhaseSourceLine; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString PreviewStabilityLine; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString DetailLine; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + TArray + RawPhaseEntries; + + 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") + bool bMatchesCurrentSelection = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bStartsOwnedCaseImmediately = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bHasExactSplitPhaseDuplicatePruningPreview = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bRetainsActiveDeckSplitPhaseDuplicatePruningProvenance = 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() + || !SurvivorCountLine.IsEmpty() + || !DuplicatePruningLine.IsEmpty() + || LaunchRequestPreviewKind + != EHyperTwistTrainingCoachGeneratedModeOptionLaunchRequestPreviewKind::None + || bCanPreview; + } +}; + +USTRUCT(BlueprintType) +struct FHyperTwistTrainingCoachGeneratedModeSelectorOptionOwnedExecutionSplitPhaseDuplicatePruningSurvivorProvenancePreviewSurface +{ + 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 RawSplitPhaseCountSummaryLine; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString DuplicatePruningSummaryLine; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString CurrentSelectionDuplicatePruningLine; + + 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 OptionsWithDuplicatePruningCount = 0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + int32 OptionsWithInvalidRawDefinitionsCount = 0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bHasSelector = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bHasOwnedExecutionSplitPhaseDuplicatePruningPreviewOptions = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bHasDirectSplitPhaseDuplicatePruningPreviewOptions = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bHasGeneratedSplitPhaseDuplicatePruningPreviewOptions = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bHasBlockedSplitPhaseDuplicatePruningPreviewOptions = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bCurrentSelectionHasExactSplitPhaseDuplicatePruningPreview = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bCurrentSelectionRetainsActiveDeckSplitPhaseDuplicatePruningProvenance = 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 + || bHasOwnedExecutionSplitPhaseDuplicatePruningPreviewOptions; + } +}; diff --git a/docs/HYPERTWIST_ROADMAP_OVERHAUL_EXPANSION_GUIDE.md b/docs/HYPERTWIST_ROADMAP_OVERHAUL_EXPANSION_GUIDE.md index 140b52a..8fcb213 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 phase-id / label-fallback provenance preview surface over the already-cached selector/runtime/request state, so gameplay/Blueprint callers can distinguish explicit labels from phase-id fallback 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 duplicate-pruning / survivor provenance preview surface on those same two consumer surfaces 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 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 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_DUPLICATE_PRUNING_SURVIVOR_PROVENANCE_PREVIEW_SURFACE_PACKET_2026-05-08.md b/docs/arch/HYPERTWIST_PHASE4_GAMEPLAY_GENERATED_MODE_SELECTOR_OPTION_OWNED_EXECUTION_SPLIT_PHASE_DUPLICATE_PRUNING_SURVIVOR_PROVENANCE_PREVIEW_SURFACE_PACKET_2026-05-08.md new file mode 100644 index 0000000..022e574 --- /dev/null +++ b/docs/arch/HYPERTWIST_PHASE4_GAMEPLAY_GENERATED_MODE_SELECTOR_OPTION_OWNED_EXECUTION_SPLIT_PHASE_DUPLICATE_PRUNING_SURVIVOR_PROVENANCE_PREVIEW_SURFACE_PACKET_2026-05-08.md @@ -0,0 +1,90 @@ +# HyperTwist Phase 4 gameplay generated-mode selector option owned execution split-phase duplicate-pruning / survivor provenance 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 phase-id / label-fallback provenance preview surface by exposing one parameterized owned execution split-phase duplicate-pruning / survivor provenance preview surface for a chosen selector id. + +The open tasks were: + +- stop forcing gameplay / Blueprint callers to rebuild timing-policy normalization duplicate-pruning rules just to see which raw split-phase definitions survive normalization +- expose direct covered-case duplicate-pruning provenance and generated owned-execution duplicate-pruning provenance on the same thin consumer lane +- expose per-raw-definition survivor and duplicate-drop provenance 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 duplicate-pruning / survivor provenance preview surfaces in shared training types +- add a coach-library helper that reconstructs raw split-phase evaluation order, survivor retention, duplicate pruning, and invalid-row skipping from the raw timing policy using the same sort / dedupe rule as timing normalization +- expose that parameterized split-phase duplicate-pruning 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 execution split-phase detail plus explicit-vs-fallback label provenance for imported selector options +- callers could already inspect retained normalized phase order and labels for a chosen option + +But one thin consumer seam was still open: + +- callers still had to reconstruct which raw split-phase definitions were dropped by duplicate rejection and which first-valid definitions actually survived normalization + +That meant: + +- split-phase detail and label-fallback preview were already thin and reusable +- but duplicate-pruning / survivor provenance still leaked timing-policy normalization rules into callers + +So the next bounded move was: + +- keep the already-landed split-phase detail and label-fallback provenance surfaces unchanged in behavior +- reuse that exact/blocked/unavailable option-state baseline and expose one narrow duplicate-pruning / survivor provenance surface above it + +## What landed + +Primary code changes: + +- `UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingTypes.h` + - added `FHyperTwistTrainingCoachGeneratedModeSelectorOptionOwnedExecutionSplitPhaseDuplicatePruningSurvivorProvenancePreviewRawPhaseEntrySurface` + - added `FHyperTwistTrainingCoachGeneratedModeSelectorOptionOwnedExecutionSplitPhaseDuplicatePruningSurvivorProvenancePreviewEntrySurface` + - added `FHyperTwistTrainingCoachGeneratedModeSelectorOptionOwnedExecutionSplitPhaseDuplicatePruningSurvivorProvenancePreviewSurface` +- `UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingCoachLibrary.h` +- `UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingCoachLibrary.cpp` + - added `DeriveCoachGeneratedModeSelectorOptionOwnedExecutionSplitPhaseDuplicatePruningSurvivorProvenancePreviewSurface(...)` + - split-phase duplicate-pruning preview is derived from cached run state plus the already-landed split-phase label-provenance preview surface and reconstructs raw evaluation/survivor/drop provenance from the raw timing policy +- `UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingPanelWidget.h` +- `UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingPanelWidget.cpp` + - added `GetDisplayedCoachGeneratedModeSelectorOptionOwnedExecutionSplitPhaseDuplicatePruningSurvivorProvenancePreviewSurface(const FString& SelectorId)` +- `UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingSessionActor.h` +- `UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingSessionActor.cpp` + - added `GetDisplayedCoachGeneratedModeSelectorOptionOwnedExecutionSplitPhaseDuplicatePruningSurvivorProvenancePreviewSurface(const FString& SelectorId)` + +Consumer result: + +- gameplay / Blueprint callers can now inspect raw split-phase definitions, survivor counts, duplicate-pruned rows, invalid rows, and per-row survivor/drop provenance for a chosen selector option without rebuilding timing-policy normalization behavior + +## Validation + +- full `Development Editor|Win64` solution build for `C:\HyperTwist\UnrealHyperTwist\UnrealHyperTwist.sln` +- result: `0` warnings, `0` errors