Expose gameplay selector split-phase normalization summary surface

This commit is contained in:
axiomlogicnexus 2026-05-08 21:09:58 +02:00
parent 9d91cc37af
commit b91829af87
9 changed files with 697 additions and 2 deletions

View file

@ -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<FString, int32> 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,

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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