Expose gameplay selector owned-execution prompt preview surface

This commit is contained in:
axiomlogicnexus 2026-05-08 15:15:17 +02:00
parent d1f0708b9c
commit 7da3d8ca8e
9 changed files with 789 additions and 2 deletions

View file

@ -11703,3 +11703,463 @@ UHyperTwistTrainingCoachLibrary::DeriveCoachGeneratedModeSelectorOptionOwnedExec
return Surface;
}
FHyperTwistTrainingCoachGeneratedModeSelectorOptionOwnedExecutionPromptPreviewSurface
UHyperTwistTrainingCoachLibrary::DeriveCoachGeneratedModeSelectorOptionOwnedExecutionPromptPreviewSurface(
const FHyperTwistTrainingRunState& RunState,
const FString& SelectorId,
const FHyperTwistTrainingCoachGeneratedModeSelectorOptionMenuSurface& OptionMenuSurface,
const FHyperTwistTrainingCoachGeneratedModeSelectorOptionLaunchRequestPreviewSurface&
OptionLaunchRequestPreviewSurface
)
{
FHyperTwistTrainingCoachGeneratedModeSelectorOptionOwnedExecutionPromptPreviewSurface Surface;
Surface.SelectorId = SelectorId;
Surface.SelectorLabel = OptionMenuSurface.SelectorLabel;
Surface.BindingLabel = OptionMenuSurface.BindingLabel;
Surface.Headline = TEXT("Generated Mode Selector Option Owned-Execution Prompt Preview");
Surface.bHasSelector = OptionMenuSurface.bHasSelector;
if (!SelectorId.IsEmpty())
{
Surface.Headline = OptionMenuSurface.SelectorLabel.IsEmpty()
? FString::Printf(
TEXT("Generated Mode Selector Option Owned-Execution Prompt Preview: %s"),
*SelectorId
)
: FString::Printf(
TEXT("Generated Mode Selector Option Owned-Execution Prompt Preview: %s"),
*OptionMenuSurface.SelectorLabel
);
}
if (SelectorId.IsEmpty())
{
Surface.StatusLine =
TEXT("Generated-mode selector option owned-execution prompt preview requires a selector id from the active selector roster.");
Surface.PromptLabelSummaryLine = TEXT("Prompt label: n/a");
Surface.PromptKindSummaryLine = TEXT("Prompt kind: n/a");
Surface.CurrentSelectionPromptLine = TEXT("Current selection: n/a");
Surface.DetailLine = OptionLaunchRequestPreviewSurface.StatusLine.IsEmpty()
? TEXT("Launch-request preview: n/a")
: FString::Printf(
TEXT("Launch-request preview: %s"),
*OptionLaunchRequestPreviewSurface.StatusLine
);
return Surface;
}
const FHyperTwistTrainingImportedRuntimeSurface& RuntimeSurface =
RunState.ActiveDeck.ImportedRuntimeSurface;
const bool bHasGeneratedModeSurface = RuntimeSurface.IsStructurallyValid()
&& RuntimeSurface.SurfaceKind.Equals(TEXT("generated-mode"), ESearchCase::IgnoreCase);
const FHyperTwistTrainingCase* SeedCase = RunState.ActiveDeck.Cases.FindByPredicate(
[](const FHyperTwistTrainingCase& Candidate)
{
return Candidate.IsStructurallyValid();
}
);
const auto DescribePromptKind = [](const EHyperTwistTrainingPromptKind PromptKind) -> FString
{
switch (PromptKind)
{
case EHyperTwistTrainingPromptKind::Algorithm:
return TEXT("Algorithm");
case EHyperTwistTrainingPromptKind::State:
return TEXT("State");
case EHyperTwistTrainingPromptKind::Recognition:
return TEXT("Recognition");
case EHyperTwistTrainingPromptKind::Memo:
return TEXT("Memo");
case EHyperTwistTrainingPromptKind::Sequence:
return TEXT("Sequence");
default:
return TEXT("Unknown");
}
};
const auto ResolveGeneratedModePromptKind = [](
const FHyperTwistTrainingCase& InSeedCase,
const FString& RuntimeModeId,
const FString& GeneratorType
) -> EHyperTwistTrainingPromptKind
{
const FString NormalizedRuntimeModeId = RuntimeModeId.ToLower();
const FString NormalizedGeneratorType = GeneratorType.ToLower();
if (NormalizedRuntimeModeId.Contains(TEXT("recognition"))
|| NormalizedGeneratorType.Contains(TEXT("recognition")))
{
return EHyperTwistTrainingPromptKind::Recognition;
}
if (NormalizedRuntimeModeId.Contains(TEXT("state"))
|| NormalizedGeneratorType.Contains(TEXT("state"))
|| NormalizedRuntimeModeId.Contains(TEXT("scramble"))
|| NormalizedGeneratorType.Contains(TEXT("scramble")))
{
return EHyperTwistTrainingPromptKind::State;
}
if ((InSeedCase.PromptKind == EHyperTwistTrainingPromptKind::Algorithm
|| InSeedCase.PromptKind == EHyperTwistTrainingPromptKind::Sequence
|| InSeedCase.PromptKind == EHyperTwistTrainingPromptKind::Memo)
&& InSeedCase.CanonicalNotation.IsEmpty())
{
return EHyperTwistTrainingPromptKind::State;
}
return InSeedCase.PromptKind;
};
const auto BuildSelectorSummaryFromPreviewEntry =
[](const FHyperTwistTrainingCoachGeneratedModeSelectorOptionLaunchRequestPreviewEntrySurface&
PreviewEntry) -> FString
{
if (PreviewEntry.SelectorChoiceEntries.Num() <= 0)
{
return TEXT("no selector choices");
}
TArray<FString> SelectorParts;
for (const FHyperTwistTrainingCoachGeneratedModeSelectorOptionLaunchRequestSelectorChoiceEntrySurface&
ChoiceEntry : PreviewEntry.SelectorChoiceEntries)
{
const FString Label = ChoiceEntry.SelectorLabel.IsEmpty()
? (ChoiceEntry.SelectorId.IsEmpty() ? FString(TEXT("n/a")) : ChoiceEntry.SelectorId)
: ChoiceEntry.SelectorLabel;
const FString Value = ChoiceEntry.DisplayValue.IsEmpty()
? (ChoiceEntry.SelectedValue.IsEmpty() ? FString(TEXT("n/a")) : ChoiceEntry.SelectedValue)
: ChoiceEntry.DisplayValue;
SelectorParts.Add(FString::Printf(TEXT("%s=%s"), *Label, *Value));
}
return SelectorParts.Num() > 0 ? FString::Join(SelectorParts, TEXT(" | "))
: TEXT("no selector choices");
};
const auto BuildGeneratedPromptLabel = [](
const FString& PromptBaseLabel,
const FString& SelectorSummary
) -> FString
{
return !PromptBaseLabel.IsEmpty() && !SelectorSummary.IsEmpty()
? FString::Printf(TEXT("%s | %s"), *PromptBaseLabel, *SelectorSummary)
: PromptBaseLabel;
};
const auto BuildPromptLabelLine = [](const FString& PromptLabel) -> FString
{
return PromptLabel.IsEmpty()
? TEXT("Prompt label: none")
: FString::Printf(TEXT("Prompt label: %s"), *PromptLabel);
};
const auto BuildPromptKindLine = [&DescribePromptKind](
const EHyperTwistTrainingPromptKind PromptKind
) -> FString
{
return FString::Printf(
TEXT("Prompt kind: %s"),
*DescribePromptKind(PromptKind)
);
};
const auto IncrementPromptKindCount = [&Surface](
const EHyperTwistTrainingPromptKind PromptKind
)
{
switch (PromptKind)
{
case EHyperTwistTrainingPromptKind::Algorithm:
++Surface.AlgorithmPromptKindOptionCount;
break;
case EHyperTwistTrainingPromptKind::State:
++Surface.StatePromptKindOptionCount;
break;
case EHyperTwistTrainingPromptKind::Recognition:
++Surface.RecognitionPromptKindOptionCount;
break;
case EHyperTwistTrainingPromptKind::Memo:
++Surface.MemoPromptKindOptionCount;
break;
case EHyperTwistTrainingPromptKind::Sequence:
++Surface.SequencePromptKindOptionCount;
break;
default:
break;
}
};
if (!RunState.IsStructurallyValid() || !bHasGeneratedModeSurface)
{
Surface.StatusLine = OptionMenuSurface.StatusLine.IsEmpty()
? TEXT("Generated-mode selector option owned-execution prompt preview is not currently available.")
: OptionMenuSurface.StatusLine;
Surface.PromptLabelSummaryLine = TEXT("Prompt label: n/a");
Surface.PromptKindSummaryLine = TEXT("Prompt kind: n/a");
Surface.CurrentSelectionPromptLine = OptionMenuSurface.bHasUnresolvedSelectionDetail
? OptionMenuSurface.UnresolvedSelectionLine
: (OptionMenuSurface.bHasSelection
? TEXT("Current selection remains cached, but no active owned-execution prompt preview is available.")
: TEXT("Current selection: unresolved"));
Surface.DetailLine = FString::Printf(
TEXT("Launch-request preview: %s"),
OptionLaunchRequestPreviewSurface.StatusLine.IsEmpty()
? TEXT("n/a")
: *OptionLaunchRequestPreviewSurface.StatusLine
);
return Surface;
}
for (const FHyperTwistTrainingCoachGeneratedModeSelectorOptionLaunchRequestPreviewEntrySurface&
PreviewEntry : OptionLaunchRequestPreviewSurface.OptionEntries)
{
FHyperTwistTrainingCoachGeneratedModeSelectorOptionOwnedExecutionPromptPreviewEntrySurface
Entry;
Entry.OptionId = PreviewEntry.OptionId;
Entry.Label = PreviewEntry.Label;
Entry.OptionValue = PreviewEntry.OptionValue;
Entry.LaunchRequestPreviewKind = PreviewEntry.LaunchRequestPreviewKind;
Entry.bMatchesCurrentSelection = PreviewEntry.bMatchesCurrentSelection;
Entry.bStartsOwnedCaseImmediately = PreviewEntry.bStartsOwnedCaseImmediately;
switch (PreviewEntry.LaunchRequestPreviewKind)
{
case EHyperTwistTrainingCoachGeneratedModeOptionLaunchRequestPreviewKind::
DirectOwnedCaseStart:
{
FHyperTwistTrainingCase MatchingCase;
if (UHyperTwistTrainingCatalogLibrary::TryGetImportedRuntimeSelectorOptionCaseFromDeck(
RunState.ActiveDeck,
SelectorId,
PreviewEntry.OptionId,
MatchingCase))
{
Entry.PreviewPromptLabel = MatchingCase.PromptLabel.IsEmpty()
? MatchingCase.CaseId
: MatchingCase.PromptLabel;
Entry.PreviewPromptKind = MatchingCase.PromptKind;
Entry.PreviewPromptKindLabel = DescribePromptKind(MatchingCase.PromptKind);
Entry.PromptLabelLine = BuildPromptLabelLine(Entry.PreviewPromptLabel);
Entry.PromptKindLine = BuildPromptKindLine(MatchingCase.PromptKind);
Entry.PromptSourceLine =
TEXT("Prompt source: exact direct covered-case prompt presentation.");
Entry.PreviewStabilityLine =
TEXT("Preview stability: exact direct owned-case prompt preview from imported option coverage.");
Entry.bHasExactPromptPreview = true;
Entry.bHasPromptLabelText = !Entry.PreviewPromptLabel.IsEmpty();
Entry.bCanPreview = true;
++Surface.DirectPromptPreviewOptionCount;
IncrementPromptKindCount(MatchingCase.PromptKind);
}
else
{
Entry.PromptLabelLine = TEXT("Prompt label: unavailable");
Entry.PromptKindLine = TEXT("Prompt kind: unavailable");
Entry.PromptSourceLine =
TEXT("Prompt source: unavailable because the covered case could not be re-resolved from the active deck.");
Entry.PreviewStabilityLine = TEXT("Preview stability: unavailable");
Entry.bCanPreview = false;
++Surface.UnavailableOptionCount;
}
break;
}
case EHyperTwistTrainingCoachGeneratedModeOptionLaunchRequestPreviewKind::
StoredRequestReusable:
case EHyperTwistTrainingCoachGeneratedModeOptionLaunchRequestPreviewKind::
RefreshRequired:
if (SeedCase == nullptr)
{
Entry.PromptLabelLine = TEXT("Prompt label: unavailable");
Entry.PromptKindLine = TEXT("Prompt kind: unavailable");
Entry.PromptSourceLine =
TEXT("Prompt source: unavailable because the active generated-mode surface has no structurally valid seed case.");
Entry.PreviewStabilityLine = TEXT("Preview stability: unavailable");
Entry.bCanPreview = false;
++Surface.UnavailableOptionCount;
break;
}
Entry.PreviewPromptKind = ResolveGeneratedModePromptKind(
*SeedCase,
PreviewEntry.HypotheticalRuntimeModeId,
PreviewEntry.HypotheticalGeneratorType
);
Entry.PreviewPromptKindLabel = DescribePromptKind(Entry.PreviewPromptKind);
Entry.PreviewPromptLabel = BuildGeneratedPromptLabel(
!PreviewEntry.HypotheticalSurfaceLabel.IsEmpty()
? PreviewEntry.HypotheticalSurfaceLabel
: RunState.ActiveDeck.Title,
BuildSelectorSummaryFromPreviewEntry(PreviewEntry)
);
Entry.PromptLabelLine = BuildPromptLabelLine(Entry.PreviewPromptLabel);
Entry.PromptKindLine = BuildPromptKindLine(Entry.PreviewPromptKind);
Entry.PromptSourceLine =
TEXT("Prompt source: exact generated-mode owned execution prompt composition from the active surface and selector payload.");
Entry.PreviewStabilityLine =
PreviewEntry.LaunchRequestPreviewKind
== EHyperTwistTrainingCoachGeneratedModeOptionLaunchRequestPreviewKind::
StoredRequestReusable
? TEXT("Preview stability: exact while execute reuses the current stored request.")
: TEXT("Preview stability: exact even though execute still needs launch-request refresh.");
Entry.bHasExactPromptPreview = true;
Entry.bHasPromptLabelText = !Entry.PreviewPromptLabel.IsEmpty();
Entry.bCanPreview = true;
++Surface.GeneratedPromptPreviewOptionCount;
IncrementPromptKindCount(Entry.PreviewPromptKind);
break;
case EHyperTwistTrainingCoachGeneratedModeOptionLaunchRequestPreviewKind::
UnresolvedAfterChoice:
Entry.PromptLabelLine =
TEXT("Prompt label: blocked until every required selector is resolved.");
Entry.PromptKindLine =
TEXT("Prompt kind: blocked until every required selector is resolved.");
Entry.PromptSourceLine =
TEXT("Prompt source: blocked while selector coverage remains incomplete.");
Entry.PreviewStabilityLine =
TEXT("Preview stability: blocked while selector coverage remains incomplete.");
Entry.bPreviewBlocked = true;
Entry.bCanPreview = true;
++Surface.BlockedOptionCount;
break;
case EHyperTwistTrainingCoachGeneratedModeOptionLaunchRequestPreviewKind::
PayloadUnavailable:
Entry.PromptLabelLine =
TEXT("Prompt label: blocked until a structurally valid launch payload can be derived.");
Entry.PromptKindLine =
TEXT("Prompt kind: blocked until a structurally valid launch payload can be derived.");
Entry.PromptSourceLine =
TEXT("Prompt source: blocked while the hypothetical generated-mode payload remains structurally invalid.");
Entry.PreviewStabilityLine =
TEXT("Preview stability: blocked while the hypothetical generated-mode payload remains structurally invalid.");
Entry.bPreviewBlocked = true;
Entry.bCanPreview = true;
++Surface.BlockedOptionCount;
break;
case EHyperTwistTrainingCoachGeneratedModeOptionLaunchRequestPreviewKind::Unavailable:
case EHyperTwistTrainingCoachGeneratedModeOptionLaunchRequestPreviewKind::None:
default:
Entry.PromptLabelLine = TEXT("Prompt label: unavailable");
Entry.PromptKindLine = TEXT("Prompt kind: unavailable");
Entry.PromptSourceLine = TEXT("Prompt source: unavailable");
Entry.PreviewStabilityLine = TEXT("Preview stability: unavailable");
Entry.bCanPreview = false;
++Surface.UnavailableOptionCount;
break;
}
Entry.DetailLine = PreviewEntry.DetailLine.IsEmpty()
? Entry.PreviewStabilityLine
: FString::Printf(
TEXT("%s | %s"),
*Entry.PreviewStabilityLine,
*PreviewEntry.DetailLine
);
if (Entry.bMatchesCurrentSelection)
{
Surface.bCurrentSelectionHasExactPromptPreview = Entry.bHasExactPromptPreview;
Surface.bCurrentSelectionHasPromptLabelText = Entry.bHasPromptLabelText;
Surface.CurrentSelectionPromptLine = FString::Printf(
TEXT("%s | %s"),
*Entry.PromptLabelLine,
*Entry.PromptKindLine
);
}
Surface.OptionEntries.Add(MoveTemp(Entry));
}
Surface.bHasOwnedExecutionPromptPreviewOptions = Surface.OptionEntries.Num() > 0;
Surface.bHasDirectPromptPreviewOptions = Surface.DirectPromptPreviewOptionCount > 0;
Surface.bHasGeneratedPromptPreviewOptions =
Surface.GeneratedPromptPreviewOptionCount > 0;
Surface.bHasBlockedPromptPreviewOptions = Surface.BlockedOptionCount > 0;
Surface.PromptLabelSummaryLine = Surface.OptionEntries.Num() > 0
? FString::Printf(
TEXT("Prompt label: %d direct-case exact | %d generated exact | %d blocked | %d unavailable"),
Surface.DirectPromptPreviewOptionCount,
Surface.GeneratedPromptPreviewOptionCount,
Surface.BlockedOptionCount,
Surface.UnavailableOptionCount
)
: TEXT("Prompt label: no imported options");
Surface.PromptKindSummaryLine = Surface.OptionEntries.Num() > 0
? FString::Printf(
TEXT("Prompt kind: %d algorithm | %d state | %d recognition | %d memo | %d sequence"),
Surface.AlgorithmPromptKindOptionCount,
Surface.StatePromptKindOptionCount,
Surface.RecognitionPromptKindOptionCount,
Surface.MemoPromptKindOptionCount,
Surface.SequencePromptKindOptionCount
)
: TEXT("Prompt kind: no imported options");
if (Surface.CurrentSelectionPromptLine.IsEmpty())
{
if (OptionMenuSurface.bHasUnresolvedSelectionDetail)
{
Surface.CurrentSelectionPromptLine = OptionMenuSurface.UnresolvedSelectionLine;
}
else if (OptionMenuSurface.bHasSelection)
{
Surface.CurrentSelectionPromptLine =
TEXT("Current selection has no additional owned-execution prompt preview.");
}
else if (OptionMenuSurface.bHasSelector)
{
Surface.CurrentSelectionPromptLine = TEXT("Current selection: unresolved");
}
else
{
Surface.CurrentSelectionPromptLine = TEXT("Current selection: n/a");
}
}
if (OptionMenuSurface.bHasSelector && Surface.OptionEntries.Num() > 0)
{
Surface.StatusLine = FString::Printf(
TEXT("Generated-mode selector option owned-execution prompt preview exposes %d direct-case exact option%s, %d generated exact option%s, %d blocked option%s, and %d unavailable option%s."),
Surface.DirectPromptPreviewOptionCount,
Surface.DirectPromptPreviewOptionCount == 1 ? TEXT("") : TEXT("s"),
Surface.GeneratedPromptPreviewOptionCount,
Surface.GeneratedPromptPreviewOptionCount == 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 prompt preview is active, but this selector does not expose imported options.");
}
else
{
Surface.StatusLine = OptionMenuSurface.StatusLine.IsEmpty()
? TEXT("Generated-mode selector option owned-execution prompt preview is not currently available.")
: OptionMenuSurface.StatusLine;
}
const FString SelectorSummary = Surface.SelectorLabel.IsEmpty()
? (Surface.SelectorId.IsEmpty() ? FString(TEXT("n/a")) : Surface.SelectorId)
: Surface.SelectorLabel;
const FString BindingSummary = Surface.BindingLabel.IsEmpty()
? FString(TEXT("n/a"))
: Surface.BindingLabel;
Surface.DetailLine = FString::Printf(
TEXT("Launch-request preview: %s | Selector: %s | Binding: %s"),
OptionLaunchRequestPreviewSurface.StatusLine.IsEmpty()
? TEXT("n/a")
: *OptionLaunchRequestPreviewSurface.StatusLine,
*SelectorSummary,
*BindingSummary
);
return Surface;
}

View file

@ -891,6 +891,25 @@ UHyperTwistTrainingPanelWidget::GetDisplayedCoachGeneratedModeSelectorOptionOwne
);
}
FHyperTwistTrainingCoachGeneratedModeSelectorOptionOwnedExecutionPromptPreviewSurface
UHyperTwistTrainingPanelWidget::GetDisplayedCoachGeneratedModeSelectorOptionOwnedExecutionPromptPreviewSurface(
const FString& SelectorId
) const
{
const FHyperTwistTrainingCoachGeneratedModeSelectorOptionMenuSurface OptionMenuSurface =
GetDisplayedCoachGeneratedModeSelectorOptionMenuSurface(SelectorId);
const FHyperTwistTrainingCoachGeneratedModeSelectorOptionLaunchRequestPreviewSurface
OptionLaunchRequestPreviewSurface =
GetDisplayedCoachGeneratedModeSelectorOptionLaunchRequestPreviewSurface(SelectorId);
return UHyperTwistTrainingCoachLibrary::
DeriveCoachGeneratedModeSelectorOptionOwnedExecutionPromptPreviewSurface(
CachedRunState,
SelectorId,
OptionMenuSurface,
OptionLaunchRequestPreviewSurface
);
}
FHyperTwistTrainingRunState UHyperTwistTrainingPanelWidget::StartCoachRecommendedRun(
const int32 MaxCases,
const FString& StartActionSourceLabel

View file

@ -872,6 +872,25 @@ AHyperTwistTrainingSessionActor::GetDisplayedCoachGeneratedModeSelectorOptionOwn
);
}
FHyperTwistTrainingCoachGeneratedModeSelectorOptionOwnedExecutionPromptPreviewSurface
AHyperTwistTrainingSessionActor::GetDisplayedCoachGeneratedModeSelectorOptionOwnedExecutionPromptPreviewSurface(
const FString& SelectorId
) const
{
const FHyperTwistTrainingCoachGeneratedModeSelectorOptionMenuSurface OptionMenuSurface =
GetDisplayedCoachGeneratedModeSelectorOptionMenuSurface(SelectorId);
const FHyperTwistTrainingCoachGeneratedModeSelectorOptionLaunchRequestPreviewSurface
OptionLaunchRequestPreviewSurface =
GetDisplayedCoachGeneratedModeSelectorOptionLaunchRequestPreviewSurface(SelectorId);
return UHyperTwistTrainingCoachLibrary::
DeriveCoachGeneratedModeSelectorOptionOwnedExecutionPromptPreviewSurface(
CachedRunState,
SelectorId,
OptionMenuSurface,
OptionLaunchRequestPreviewSurface
);
}
FHyperTwistTrainingRunState AHyperTwistTrainingSessionActor::StartCoachRecommendedTrainingRun(
const int32 MaxCases
)

View file

@ -787,4 +787,14 @@ public:
const FHyperTwistTrainingCoachGeneratedModeSelectorOptionLaunchRequestEnvelopePreviewSurface&
OptionLaunchRequestEnvelopePreviewSurface
);
UFUNCTION(BlueprintPure, Category = "HyperTwist|Training|Coach")
static FHyperTwistTrainingCoachGeneratedModeSelectorOptionOwnedExecutionPromptPreviewSurface
DeriveCoachGeneratedModeSelectorOptionOwnedExecutionPromptPreviewSurface(
const FHyperTwistTrainingRunState& RunState,
const FString& SelectorId,
const FHyperTwistTrainingCoachGeneratedModeSelectorOptionMenuSurface& OptionMenuSurface,
const FHyperTwistTrainingCoachGeneratedModeSelectorOptionLaunchRequestPreviewSurface&
OptionLaunchRequestPreviewSurface
);
};

View file

@ -302,6 +302,12 @@ public:
const FString& SelectorId
) const;
UFUNCTION(BlueprintPure, Category = "HyperTwist|Training|Coach")
FHyperTwistTrainingCoachGeneratedModeSelectorOptionOwnedExecutionPromptPreviewSurface
GetDisplayedCoachGeneratedModeSelectorOptionOwnedExecutionPromptPreviewSurface(
const FString& SelectorId
) const;
UFUNCTION(BlueprintCallable, Category = "HyperTwist|Training|Coach")
FHyperTwistTrainingRunState StartCoachRecommendedRun(
int32 MaxCases,

View file

@ -283,6 +283,12 @@ public:
const FString& SelectorId
) const;
UFUNCTION(BlueprintPure, Category = "HyperTwist|Training|Coach")
FHyperTwistTrainingCoachGeneratedModeSelectorOptionOwnedExecutionPromptPreviewSurface
GetDisplayedCoachGeneratedModeSelectorOptionOwnedExecutionPromptPreviewSurface(
const FString& SelectorId
) const;
UFUNCTION(BlueprintCallable, Category = "HyperTwist|Training|Coach")
FHyperTwistTrainingRunState StartCoachRecommendedTrainingRun(int32 MaxCases);

View file

@ -10310,3 +10310,169 @@ struct FHyperTwistTrainingCoachGeneratedModeSelectorOptionOwnedExecutionCasePrev
|| bHasOwnedExecutionCasePreviewOptions;
}
};
USTRUCT(BlueprintType)
struct FHyperTwistTrainingCoachGeneratedModeSelectorOptionOwnedExecutionPromptPreviewEntrySurface
{
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 PromptLabelLine;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString PromptKindLine;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString PromptSourceLine;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString PreviewStabilityLine;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString DetailLine;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString PreviewPromptLabel;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
EHyperTwistTrainingPromptKind PreviewPromptKind = EHyperTwistTrainingPromptKind::Algorithm;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString PreviewPromptKindLabel;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
bool bMatchesCurrentSelection = false;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
bool bStartsOwnedCaseImmediately = false;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
bool bHasExactPromptPreview = false;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
bool bHasPromptLabelText = 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()
|| !PromptLabelLine.IsEmpty()
|| !PromptKindLine.IsEmpty()
|| LaunchRequestPreviewKind
!= EHyperTwistTrainingCoachGeneratedModeOptionLaunchRequestPreviewKind::None
|| bCanPreview;
}
};
USTRUCT(BlueprintType)
struct FHyperTwistTrainingCoachGeneratedModeSelectorOptionOwnedExecutionPromptPreviewSurface
{
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 PromptLabelSummaryLine;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString PromptKindSummaryLine;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString CurrentSelectionPromptLine;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString DetailLine;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
TArray<FHyperTwistTrainingCoachGeneratedModeSelectorOptionOwnedExecutionPromptPreviewEntrySurface>
OptionEntries;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
int32 DirectPromptPreviewOptionCount = 0;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
int32 GeneratedPromptPreviewOptionCount = 0;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
int32 BlockedOptionCount = 0;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
int32 UnavailableOptionCount = 0;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
int32 AlgorithmPromptKindOptionCount = 0;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
int32 StatePromptKindOptionCount = 0;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
int32 RecognitionPromptKindOptionCount = 0;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
int32 MemoPromptKindOptionCount = 0;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
int32 SequencePromptKindOptionCount = 0;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
bool bHasSelector = false;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
bool bHasOwnedExecutionPromptPreviewOptions = false;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
bool bHasDirectPromptPreviewOptions = false;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
bool bHasGeneratedPromptPreviewOptions = false;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
bool bHasBlockedPromptPreviewOptions = false;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
bool bCurrentSelectionHasExactPromptPreview = false;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
bool bCurrentSelectionHasPromptLabelText = false;
bool IsStructurallyValid() const
{
return !StatusLine.IsEmpty()
|| !SelectorId.IsEmpty()
|| OptionEntries.Num() > 0
|| bHasSelector
|| bHasOwnedExecutionPromptPreviewOptions;
}
};

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 case-identity / canonical-state preview surface over the already-cached selector/runtime/request state, so gameplay/Blueprint callers can render exact direct-case targets, exact stored-request-derived generated case ids, and refresh-template canonical-state refs for a chosen option without rebuilding executor naming rules directly
- the current next bounded packet is to expose one thin generated-mode selector option owned execution prompt-label / prompt-kind preview surface on those same two consumer surfaces so gameplay/Blueprint callers can render the resulting owned prompt presentation for a chosen option without rebuilding generated deck composition rules directly
- the latest landed bounded `Phase 4` packet stays on that same gameplay-facing consumer lane and adds one thin generated-mode selector option owned execution prompt-label / prompt-kind preview surface over the already-cached selector/runtime/request state, so gameplay/Blueprint callers can render exact direct-case prompt presentation and exact generated owned prompt composition for a chosen option without rebuilding generated deck composition rules directly
- the current next bounded packet is to expose one thin generated-mode selector option owned execution delivery-mode / reveal-posture / time-target preview surface on those same two consumer surfaces so gameplay/Blueprint callers can render the resulting owned execution modality and reveal posture for a chosen option without rebuilding generated deck composition rules directly
- the current execution-discipline rule that broad refactor / monolith-splitting work should not interrupt the active bounded roadmap packet unless structure is actually blocking it
## Current execution-reality references

View file

@ -0,0 +1,101 @@
# HyperTwist Phase 4 gameplay generated-mode selector option owned execution prompt-label / prompt-kind 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 case-identity / canonical-state preview surface by exposing one parameterized owned execution prompt-label / prompt-kind preview surface for a chosen selector id.
The open tasks were:
- stop forcing gameplay / Blueprint callers to rebuild generated deck prompt composition rules just to explain the resulting owned prompt presentation for a chosen imported option
- expose exact direct covered-case prompt presentation plus exact generated owned prompt composition whenever the generated-mode payload is previewable
- keep the surface derived from already-cached gameplay consumer run, selector, and launch-request preview state instead of widening into backend logic
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 prompt preview surfaces in shared training types
- add a coach-library helper that projects prompt-label and prompt-kind preview over the already-landed launch-request preview surface
- expose that parameterized prompt 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 launch payload, request reuse, envelope identity, owned case identity, and canonical-state preview for imported selector options
- callers could tell which owned case target and canonical-state ref would result from a chosen option
But one thin consumer seam was still open:
- callers still had to rebuild generated prompt-label and prompt-kind composition from seed-case rules, runtime mode, generator type, surface label, and selector summary if they wanted to describe the resulting owned prompt presentation
That meant:
- target identity was already thin and reusable
- but visible prompt presentation still leaked generated deck composition rules into callers
So the next bounded move was:
- keep the already-landed owned case / canonical-state preview surface unchanged in behavior
- reuse the already-cached launch-request preview surface to expose one narrow owned execution prompt preview surface above it
## What landed
Primary code changes:
- `UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingTypes.h`
- added `FHyperTwistTrainingCoachGeneratedModeSelectorOptionOwnedExecutionPromptPreviewEntrySurface`
- added `FHyperTwistTrainingCoachGeneratedModeSelectorOptionOwnedExecutionPromptPreviewSurface`
- `UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingCoachLibrary.h`
- `UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingCoachLibrary.cpp`
- added `DeriveCoachGeneratedModeSelectorOptionOwnedExecutionPromptPreviewSurface(...)`
- prompt preview is derived from cached run state plus the already-landed launch-request preview surface
- `UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingPanelWidget.h`
- `UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingPanelWidget.cpp`
- added `GetDisplayedCoachGeneratedModeSelectorOptionOwnedExecutionPromptPreviewSurface(const FString& SelectorId)`
- `UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingSessionActor.h`
- `UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingSessionActor.cpp`
- added `GetDisplayedCoachGeneratedModeSelectorOptionOwnedExecutionPromptPreviewSurface(const FString& SelectorId)`
Surface behavior:
- reports exact direct covered-case prompt label and prompt kind for selector options that dispatch straight to owned cases
- reports exact generated owned prompt label and prompt kind whenever the chosen option yields a previewable generated-mode payload, including refresh-required options whose request id has not been persisted yet
- keeps unresolved or payload-invalid choices inspectable as blocked prompt-preview states instead of collapsing them into a generic unavailable branch
- keeps the surface presentation-only and compatible with the already-landed launch / execution, request-alignment, stored-request delta, launch-request payload / refresh-preview, launch-request metadata / origin-notes preview, launch-request envelope / request-source preview, and owned execution case / canonical-state preview surfaces
## Validation
Validation target:
- full `Development Editor|Win64` build of `C:\HyperTwist\UnrealHyperTwist\UnrealHyperTwist.sln`
Result:
- passed with `0` warnings and `0` errors
## Follow-on
The next bounded packet on this same gameplay-facing consumer lane should stay thin and expose one generated-mode selector option owned execution delivery-mode / reveal-posture / time-target preview surface so callers can render the resulting owned execution modality and reveal posture for a chosen option without rebuilding generated deck composition rules directly.