Expose gameplay selector launch-request metadata preview surface

This commit is contained in:
axiomlogicnexus 2026-05-08 05:13:31 +02:00
parent 8fff384597
commit 4ce2d0eaa9
9 changed files with 673 additions and 2 deletions

View file

@ -10463,3 +10463,334 @@ UHyperTwistTrainingCoachLibrary::DeriveCoachGeneratedModeSelectorOptionLaunchReq
return Surface;
}
FHyperTwistTrainingCoachGeneratedModeSelectorOptionLaunchRequestMetadataPreviewSurface
UHyperTwistTrainingCoachLibrary::DeriveCoachGeneratedModeSelectorOptionLaunchRequestMetadataPreviewSurface(
const FHyperTwistTrainingRunState& RunState,
const FString& SelectorId,
const FHyperTwistTrainingCoachGeneratedModeSelectorOptionMenuSurface& OptionMenuSurface,
const FHyperTwistTrainingCoachGeneratedModeSelectorOptionLaunchRequestPreviewSurface&
OptionLaunchRequestPreviewSurface
)
{
FHyperTwistTrainingCoachGeneratedModeSelectorOptionLaunchRequestMetadataPreviewSurface Surface;
Surface.SelectorId = SelectorId;
Surface.SelectorLabel = OptionMenuSurface.SelectorLabel;
Surface.BindingLabel = OptionMenuSurface.BindingLabel;
Surface.Headline = TEXT("Generated Mode Selector Option Launch-Request Metadata Preview");
Surface.bHasSelector = OptionMenuSurface.bHasSelector;
if (!SelectorId.IsEmpty())
{
Surface.Headline = OptionMenuSurface.SelectorLabel.IsEmpty()
? FString::Printf(
TEXT("Generated Mode Selector Option Launch-Request Metadata Preview: %s"),
*SelectorId
)
: FString::Printf(
TEXT("Generated Mode Selector Option Launch-Request Metadata Preview: %s"),
*OptionMenuSurface.SelectorLabel
);
}
if (SelectorId.IsEmpty())
{
Surface.StatusLine =
TEXT("Generated-mode selector option launch-request metadata preview requires a selector id from the active selector roster.");
Surface.MetadataSummaryLine = TEXT("Metadata preview: n/a");
Surface.MetadataScopeLine = TEXT("Metadata scope: n/a");
Surface.OriginSummaryLine = TEXT("Origin paths: n/a");
Surface.NotesSummaryLine = TEXT("Notes: n/a");
Surface.CurrentSelectionMetadataLine = TEXT("Current selection: n/a");
Surface.DetailLine = OptionLaunchRequestPreviewSurface.StatusLine.IsEmpty()
? TEXT("Launch-request preview: n/a")
: FString::Printf(
TEXT("Launch-request preview: %s"),
*OptionLaunchRequestPreviewSurface.StatusLine
);
return Surface;
}
const FHyperTwistTrainingImportedRuntimeSurface& RuntimeSurface = RunState.ActiveDeck.ImportedRuntimeSurface;
const bool bHasGeneratedModeSurface = RuntimeSurface.IsStructurallyValid()
&& RuntimeSurface.SurfaceKind.Equals(TEXT("generated-mode"), ESearchCase::IgnoreCase);
const auto BuildOriginSummaryLine = [](
const TArray<FString>& OriginPaths
) -> FString
{
if (OriginPaths.Num() == 0)
{
return TEXT("Origin paths: none");
}
const int32 PreviewCount = FMath::Min(2, OriginPaths.Num());
TArray<FString> PreviewPaths;
for (int32 Index = 0; Index < PreviewCount; ++Index)
{
PreviewPaths.Add(OriginPaths[Index]);
}
return FString::Printf(
TEXT("Origin paths: %d (%s%s)"),
OriginPaths.Num(),
*FString::Join(PreviewPaths, TEXT(" | ")),
OriginPaths.Num() > PreviewCount
? *FString::Printf(TEXT(" | +%d more"), OriginPaths.Num() - PreviewCount)
: TEXT("")
);
};
const auto BuildNotesSummaryLine = [](
const FString& Notes
) -> FString
{
return Notes.IsEmpty()
? TEXT("Notes: none")
: FString::Printf(TEXT("Notes: %s"), *Notes);
};
const auto BuildMetadataScopeLine = [](
const FString& SurfaceId,
const FString& SurfaceLabel,
const FString& RuntimeModeId,
const FString& GeneratorType
) -> FString
{
const FString SurfaceSummary = SurfaceLabel.IsEmpty()
? (SurfaceId.IsEmpty() ? FString(TEXT("n/a")) : SurfaceId)
: FString::Printf(
TEXT("%s [%s]"),
*SurfaceLabel,
SurfaceId.IsEmpty() ? TEXT("n/a") : *SurfaceId
);
const FString RuntimeModeSummary = RuntimeModeId.IsEmpty() ? TEXT("n/a") : RuntimeModeId;
const FString GeneratorSummary = GeneratorType.IsEmpty() ? TEXT("n/a") : GeneratorType;
return FString::Printf(
TEXT("Metadata scope: surface=%s | mode=%s | generator=%s"),
*SurfaceSummary,
*RuntimeModeSummary,
*GeneratorSummary
);
};
Surface.OriginPaths = RuntimeSurface.OriginPaths;
Surface.Notes = RuntimeSurface.Notes;
Surface.OriginPathCount = RuntimeSurface.OriginPaths.Num();
Surface.OriginSummaryLine = BuildOriginSummaryLine(RuntimeSurface.OriginPaths);
Surface.NotesSummaryLine = BuildNotesSummaryLine(RuntimeSurface.Notes);
Surface.MetadataScopeLine = BuildMetadataScopeLine(
RuntimeSurface.SurfaceId,
RuntimeSurface.SurfaceLabel,
RuntimeSurface.RuntimeModeId,
RuntimeSurface.GeneratorType
);
if (!RunState.IsStructurallyValid() || !bHasGeneratedModeSurface)
{
Surface.StatusLine = OptionMenuSurface.StatusLine.IsEmpty()
? TEXT("Generated-mode selector option launch-request metadata preview is not currently available.")
: OptionMenuSurface.StatusLine;
Surface.MetadataSummaryLine = TEXT("Metadata preview: n/a");
Surface.MetadataScopeLine = TEXT("Metadata scope: n/a");
Surface.OriginSummaryLine = TEXT("Origin paths: n/a");
Surface.NotesSummaryLine = TEXT("Notes: n/a");
Surface.CurrentSelectionMetadataLine = OptionMenuSurface.bHasUnresolvedSelectionDetail
? OptionMenuSurface.UnresolvedSelectionLine
: (OptionMenuSurface.bHasSelection
? TEXT("Current selection remains cached, but no active launch-request metadata preview is available.")
: TEXT("Current selection: unresolved"));
Surface.DetailLine = OptionLaunchRequestPreviewSurface.StatusLine.IsEmpty()
? TEXT("Launch-request preview: n/a")
: FString::Printf(
TEXT("Launch-request preview: %s"),
*OptionLaunchRequestPreviewSurface.StatusLine
);
return Surface;
}
for (const FHyperTwistTrainingCoachGeneratedModeSelectorOptionLaunchRequestPreviewEntrySurface&
PreviewEntry : OptionLaunchRequestPreviewSurface.OptionEntries)
{
FHyperTwistTrainingCoachGeneratedModeSelectorOptionLaunchRequestMetadataPreviewEntrySurface
Entry;
Entry.OptionId = PreviewEntry.OptionId;
Entry.Label = PreviewEntry.Label;
Entry.OptionValue = PreviewEntry.OptionValue;
Entry.LaunchRequestPreviewKind = PreviewEntry.LaunchRequestPreviewKind;
Entry.PayloadIdentityLine = PreviewEntry.PayloadIdentityLine;
Entry.HypotheticalSurfaceId = PreviewEntry.HypotheticalSurfaceId.IsEmpty()
? RuntimeSurface.SurfaceId
: PreviewEntry.HypotheticalSurfaceId;
Entry.HypotheticalSurfaceLabel = PreviewEntry.HypotheticalSurfaceLabel.IsEmpty()
? RuntimeSurface.SurfaceLabel
: PreviewEntry.HypotheticalSurfaceLabel;
Entry.HypotheticalRuntimeModeId = PreviewEntry.HypotheticalRuntimeModeId.IsEmpty()
? RuntimeSurface.RuntimeModeId
: PreviewEntry.HypotheticalRuntimeModeId;
Entry.HypotheticalGeneratorType = PreviewEntry.HypotheticalGeneratorType.IsEmpty()
? RuntimeSurface.GeneratorType
: PreviewEntry.HypotheticalGeneratorType;
Entry.bMatchesCurrentSelection = PreviewEntry.bMatchesCurrentSelection;
Entry.bStartsOwnedCaseImmediately = PreviewEntry.bStartsOwnedCaseImmediately;
if (PreviewEntry.LaunchRequestPreviewKind
== EHyperTwistTrainingCoachGeneratedModeOptionLaunchRequestPreviewKind::
DirectOwnedCaseStart)
{
Entry.MetadataScopeLine =
TEXT("Metadata scope: direct owned-case launch bypasses generated-mode launch-request metadata preview.");
Entry.OriginSummaryLine =
TEXT("Origin paths: direct owned-case launch bypasses generated-mode launch-request metadata preview.");
Entry.NotesSummaryLine =
TEXT("Notes: direct owned-case launch bypasses generated-mode launch-request metadata preview.");
Entry.DetailLine = PreviewEntry.DetailLine.IsEmpty()
? TEXT("Launch-request metadata preview is not applicable because this option starts a direct owned case instead of staging a generated-mode payload.")
: PreviewEntry.DetailLine;
Entry.bCanPreview = true;
++Surface.DirectOwnedCaseStartOptionCount;
}
else if (PreviewEntry.LaunchRequestPreviewKind
== EHyperTwistTrainingCoachGeneratedModeOptionLaunchRequestPreviewKind::Unavailable
|| PreviewEntry.LaunchRequestPreviewKind
== EHyperTwistTrainingCoachGeneratedModeOptionLaunchRequestPreviewKind::None)
{
Entry.MetadataScopeLine = TEXT("Metadata scope: unavailable");
Entry.OriginSummaryLine = TEXT("Origin paths: unavailable");
Entry.NotesSummaryLine = TEXT("Notes: unavailable");
Entry.DetailLine =
TEXT("Launch-request metadata preview is unavailable because this selector option has no generated-mode payload preview from the active surface.");
Entry.bCanPreview = false;
++Surface.UnavailableOptionCount;
}
else
{
Entry.OriginPaths = RuntimeSurface.OriginPaths;
Entry.Notes = RuntimeSurface.Notes;
Entry.OriginPathCount = RuntimeSurface.OriginPaths.Num();
Entry.bHasOriginPaths = RuntimeSurface.OriginPaths.Num() > 0;
Entry.bHasNotes = !RuntimeSurface.Notes.IsEmpty();
Entry.bHasMetadataPreview = true;
Entry.bCanPreview = true;
Entry.MetadataScopeLine = BuildMetadataScopeLine(
Entry.HypotheticalSurfaceId,
Entry.HypotheticalSurfaceLabel,
Entry.HypotheticalRuntimeModeId,
Entry.HypotheticalGeneratorType
);
Entry.OriginSummaryLine = BuildOriginSummaryLine(RuntimeSurface.OriginPaths);
Entry.NotesSummaryLine = BuildNotesSummaryLine(RuntimeSurface.Notes);
Entry.DetailLine = FString::Printf(
TEXT("%s | %s"),
*Entry.MetadataScopeLine,
PreviewEntry.RefreshPreviewLine.IsEmpty()
? (PreviewEntry.StoredRequestReuseLine.IsEmpty()
? TEXT("Launch-request metadata preview stays attached to the imported generated-mode surface.")
: *PreviewEntry.StoredRequestReuseLine)
: *PreviewEntry.RefreshPreviewLine
);
++Surface.MetadataPreviewOptionCount;
if (Entry.bHasOriginPaths)
{
++Surface.OriginPathOptionCount;
}
if (Entry.bHasNotes)
{
++Surface.NotesOptionCount;
}
}
if (Entry.bMatchesCurrentSelection)
{
Surface.bCurrentSelectionHasOriginPaths = Entry.bHasOriginPaths;
Surface.bCurrentSelectionHasNotes = Entry.bHasNotes;
Surface.CurrentSelectionMetadataLine = Entry.bStartsOwnedCaseImmediately
? Entry.OriginSummaryLine
: FString::Printf(
TEXT("%s | %s"),
*Entry.OriginSummaryLine,
*Entry.NotesSummaryLine
);
}
Surface.OptionEntries.Add(MoveTemp(Entry));
}
Surface.bHasMetadataPreviewOptions = Surface.MetadataPreviewOptionCount > 0;
Surface.bHasOriginPathOptions = Surface.OriginPathOptionCount > 0;
Surface.bHasNotesOptions = Surface.NotesOptionCount > 0;
Surface.MetadataSummaryLine = Surface.OptionEntries.Num() > 0
? FString::Printf(
TEXT("Metadata preview: %d payload-backed option%s, %d with origin paths, %d with notes, %d direct-launch, %d unavailable."),
Surface.MetadataPreviewOptionCount,
Surface.MetadataPreviewOptionCount == 1 ? TEXT("") : TEXT("s"),
Surface.OriginPathOptionCount,
Surface.NotesOptionCount,
Surface.DirectOwnedCaseStartOptionCount,
Surface.UnavailableOptionCount
)
: TEXT("Metadata preview: no imported options");
if (Surface.CurrentSelectionMetadataLine.IsEmpty())
{
if (OptionMenuSurface.bHasUnresolvedSelectionDetail)
{
Surface.CurrentSelectionMetadataLine = OptionMenuSurface.UnresolvedSelectionLine;
}
else if (OptionMenuSurface.bHasSelection)
{
Surface.CurrentSelectionMetadataLine =
TEXT("Current selection has no additional launch-request metadata preview.");
}
else if (OptionMenuSurface.bHasSelector)
{
Surface.CurrentSelectionMetadataLine = TEXT("Current selection: unresolved");
}
else
{
Surface.CurrentSelectionMetadataLine = TEXT("Current selection: n/a");
}
}
if (OptionMenuSurface.bHasSelector && Surface.OptionEntries.Num() > 0)
{
Surface.StatusLine = FString::Printf(
TEXT("Generated-mode selector option launch-request metadata preview exposes %d payload-backed option%s, %d direct-launch option%s, and %d unavailable option%s."),
Surface.MetadataPreviewOptionCount,
Surface.MetadataPreviewOptionCount == 1 ? TEXT("") : TEXT("s"),
Surface.DirectOwnedCaseStartOptionCount,
Surface.DirectOwnedCaseStartOptionCount == 1 ? TEXT("") : TEXT("s"),
Surface.UnavailableOptionCount,
Surface.UnavailableOptionCount == 1 ? TEXT("") : TEXT("s")
);
}
else if (OptionMenuSurface.bHasSelector)
{
Surface.StatusLine =
TEXT("Generated-mode selector option launch-request metadata preview is active, but this selector does not expose imported options.");
}
else
{
Surface.StatusLine = OptionMenuSurface.StatusLine.IsEmpty()
? TEXT("Generated-mode selector option launch-request metadata 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

@ -834,6 +834,25 @@ UHyperTwistTrainingPanelWidget::GetDisplayedCoachGeneratedModeSelectorOptionLaun
);
}
FHyperTwistTrainingCoachGeneratedModeSelectorOptionLaunchRequestMetadataPreviewSurface
UHyperTwistTrainingPanelWidget::GetDisplayedCoachGeneratedModeSelectorOptionLaunchRequestMetadataPreviewSurface(
const FString& SelectorId
) const
{
const FHyperTwistTrainingCoachGeneratedModeSelectorOptionMenuSurface OptionMenuSurface =
GetDisplayedCoachGeneratedModeSelectorOptionMenuSurface(SelectorId);
const FHyperTwistTrainingCoachGeneratedModeSelectorOptionLaunchRequestPreviewSurface
OptionLaunchRequestPreviewSurface =
GetDisplayedCoachGeneratedModeSelectorOptionLaunchRequestPreviewSurface(SelectorId);
return UHyperTwistTrainingCoachLibrary::
DeriveCoachGeneratedModeSelectorOptionLaunchRequestMetadataPreviewSurface(
CachedRunState,
SelectorId,
OptionMenuSurface,
OptionLaunchRequestPreviewSurface
);
}
FHyperTwistTrainingRunState UHyperTwistTrainingPanelWidget::StartCoachRecommendedRun(
const int32 MaxCases,
const FString& StartActionSourceLabel

View file

@ -815,6 +815,25 @@ AHyperTwistTrainingSessionActor::GetDisplayedCoachGeneratedModeSelectorOptionLau
);
}
FHyperTwistTrainingCoachGeneratedModeSelectorOptionLaunchRequestMetadataPreviewSurface
AHyperTwistTrainingSessionActor::GetDisplayedCoachGeneratedModeSelectorOptionLaunchRequestMetadataPreviewSurface(
const FString& SelectorId
) const
{
const FHyperTwistTrainingCoachGeneratedModeSelectorOptionMenuSurface OptionMenuSurface =
GetDisplayedCoachGeneratedModeSelectorOptionMenuSurface(SelectorId);
const FHyperTwistTrainingCoachGeneratedModeSelectorOptionLaunchRequestPreviewSurface
OptionLaunchRequestPreviewSurface =
GetDisplayedCoachGeneratedModeSelectorOptionLaunchRequestPreviewSurface(SelectorId);
return UHyperTwistTrainingCoachLibrary::
DeriveCoachGeneratedModeSelectorOptionLaunchRequestMetadataPreviewSurface(
CachedRunState,
SelectorId,
OptionMenuSurface,
OptionLaunchRequestPreviewSurface
);
}
FHyperTwistTrainingRunState AHyperTwistTrainingSessionActor::StartCoachRecommendedTrainingRun(
const int32 MaxCases
)

View file

@ -757,4 +757,14 @@ public:
const FHyperTwistTrainingCoachGeneratedModeSelectorOptionRequestAlignmentSurface&
OptionRequestAlignmentSurface
);
UFUNCTION(BlueprintPure, Category = "HyperTwist|Training|Coach")
static FHyperTwistTrainingCoachGeneratedModeSelectorOptionLaunchRequestMetadataPreviewSurface
DeriveCoachGeneratedModeSelectorOptionLaunchRequestMetadataPreviewSurface(
const FHyperTwistTrainingRunState& RunState,
const FString& SelectorId,
const FHyperTwistTrainingCoachGeneratedModeSelectorOptionMenuSurface& OptionMenuSurface,
const FHyperTwistTrainingCoachGeneratedModeSelectorOptionLaunchRequestPreviewSurface&
OptionLaunchRequestPreviewSurface
);
};

View file

@ -284,6 +284,12 @@ public:
FHyperTwistTrainingCoachGeneratedModeSelectorOptionLaunchRequestPreviewSurface
GetDisplayedCoachGeneratedModeSelectorOptionLaunchRequestPreviewSurface(const FString& SelectorId) const;
UFUNCTION(BlueprintPure, Category = "HyperTwist|Training|Coach")
FHyperTwistTrainingCoachGeneratedModeSelectorOptionLaunchRequestMetadataPreviewSurface
GetDisplayedCoachGeneratedModeSelectorOptionLaunchRequestMetadataPreviewSurface(
const FString& SelectorId
) const;
UFUNCTION(BlueprintCallable, Category = "HyperTwist|Training|Coach")
FHyperTwistTrainingRunState StartCoachRecommendedRun(
int32 MaxCases,

View file

@ -265,6 +265,12 @@ public:
FHyperTwistTrainingCoachGeneratedModeSelectorOptionLaunchRequestPreviewSurface
GetDisplayedCoachGeneratedModeSelectorOptionLaunchRequestPreviewSurface(const FString& SelectorId) const;
UFUNCTION(BlueprintPure, Category = "HyperTwist|Training|Coach")
FHyperTwistTrainingCoachGeneratedModeSelectorOptionLaunchRequestMetadataPreviewSurface
GetDisplayedCoachGeneratedModeSelectorOptionLaunchRequestMetadataPreviewSurface(
const FString& SelectorId
) const;
UFUNCTION(BlueprintCallable, Category = "HyperTwist|Training|Coach")
FHyperTwistTrainingRunState StartCoachRecommendedTrainingRun(int32 MaxCases);

View file

@ -9748,3 +9748,180 @@ struct FHyperTwistTrainingCoachGeneratedModeSelectorOptionLaunchRequestPreviewSu
|| bHasLaunchRequestPreviewOptions;
}
};
USTRUCT(BlueprintType)
struct FHyperTwistTrainingCoachGeneratedModeSelectorOptionLaunchRequestMetadataPreviewEntrySurface
{
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 PayloadIdentityLine;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString MetadataScopeLine;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString OriginSummaryLine;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString NotesSummaryLine;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString DetailLine;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
TArray<FString> OriginPaths;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString Notes;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString HypotheticalSurfaceId;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString HypotheticalSurfaceLabel;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString HypotheticalRuntimeModeId;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString HypotheticalGeneratorType;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
int32 OriginPathCount = 0;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
bool bMatchesCurrentSelection = false;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
bool bStartsOwnedCaseImmediately = false;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
bool bHasOriginPaths = false;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
bool bHasNotes = false;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
bool bHasMetadataPreview = false;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
bool bCanPreview = false;
bool IsStructurallyValid() const
{
return !OptionId.IsEmpty()
|| !Label.IsEmpty()
|| !MetadataScopeLine.IsEmpty()
|| LaunchRequestPreviewKind
!= EHyperTwistTrainingCoachGeneratedModeOptionLaunchRequestPreviewKind::None
|| bCanPreview;
}
};
USTRUCT(BlueprintType)
struct FHyperTwistTrainingCoachGeneratedModeSelectorOptionLaunchRequestMetadataPreviewSurface
{
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 MetadataSummaryLine;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString MetadataScopeLine;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString OriginSummaryLine;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString NotesSummaryLine;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString CurrentSelectionMetadataLine;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString DetailLine;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
TArray<FHyperTwistTrainingCoachGeneratedModeSelectorOptionLaunchRequestMetadataPreviewEntrySurface>
OptionEntries;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
TArray<FString> OriginPaths;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString Notes;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
int32 MetadataPreviewOptionCount = 0;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
int32 OriginPathOptionCount = 0;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
int32 NotesOptionCount = 0;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
int32 OriginPathCount = 0;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
int32 DirectOwnedCaseStartOptionCount = 0;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
int32 UnavailableOptionCount = 0;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
bool bHasSelector = false;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
bool bHasMetadataPreviewOptions = false;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
bool bHasOriginPathOptions = false;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
bool bHasNotesOptions = false;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
bool bCurrentSelectionHasOriginPaths = false;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
bool bCurrentSelectionHasNotes = false;
bool IsStructurallyValid() const
{
return !StatusLine.IsEmpty()
|| !SelectorId.IsEmpty()
|| OptionEntries.Num() > 0
|| bHasSelector
|| bHasMetadataPreviewOptions;
}
};

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 hypothetical launch-request payload / refresh-preview surface over the already-cached selector/runtime state, so gameplay/Blueprint callers can render the concrete selector-choice payload, stored-request reuse posture, and request-refresh preview for a chosen option without rebuilding launch-config payloads directly
- the current next bounded packet is to expose one thin generated-mode selector option hypothetical launch-request metadata / origin-notes preview surface on those same two consumer surfaces so gameplay/Blueprint callers can render imported origin-path and notes provenance over that hypothetical payload without reading raw launch-config metadata directly
- the latest landed bounded `Phase 4` packet stays on that same gameplay-facing consumer lane and adds one thin generated-mode selector option hypothetical launch-request metadata / origin-notes preview surface over the already-cached selector/runtime state, so gameplay/Blueprint callers can render imported origin-path and notes provenance over that hypothetical payload without reading raw launch-config metadata directly
- the current next bounded packet is to expose one thin generated-mode selector option stored launch-request envelope / request-source preview surface on those same two consumer surfaces so gameplay/Blueprint callers can render request id, request source, requested-at timestamp, and reuse-vs-refresh persistence context for a chosen option without reading raw launch-request envelope fields 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,103 @@
# HyperTwist Phase 4 gameplay generated-mode selector option launch-request metadata / origin-notes 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 launch-request payload / refresh-preview surface by exposing one parameterized option launch-request metadata / origin-notes preview surface for a chosen selector id.
The open tasks were:
- stop forcing gameplay / Blueprint callers to read raw generated-mode launch-config metadata just to render imported provenance over a hypothetical payload
- expose imported origin-path and notes provenance for each imported option on the chosen selector while preserving the already-landed payload / refresh-preview posture
- keep the surface derived from already-cached gameplay consumer run, selector, runtime-surface, and launch-request preview state instead of widening into backend logic
It is not:
- a selector-dispatch behavior change
- a launch-request persistence change
- a generated-mode metadata materialization change
- a broader gameplay UI composition pass
## Scope
Bounded lane:
- add first-party generated-mode selector option launch-request metadata / origin-notes preview surfaces in shared training types
- add a coach-library helper that projects imported runtime-surface origin paths and notes over the already-landed hypothetical launch-request preview posture
- expose that parameterized metadata 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 generated-mode launch-config composition
- changing stored launch-request creation, refresh, or execute behavior
- 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 hypothetical payload identity, selector payload, stored-request reuse posture, and refresh preview
- those same consumers already exposed truthful request-alignment and stored-request delta posture
But one thin consumer seam was still open:
- callers could explain what hypothetical payload would be staged
- but they still had to reach into raw launch-config metadata to render imported origin paths and notes over that payload
That meant:
- payload preview posture was thin and reusable
- but metadata provenance still leaked raw launch-config field access into callers
So the next bounded move was:
- keep the already-landed payload / refresh-preview surface unchanged
- and expose one narrow metadata / origin-notes projection above that cached launch-request preview
## What landed
Primary code changes:
- `UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingTypes.h`
- added `FHyperTwistTrainingCoachGeneratedModeSelectorOptionLaunchRequestMetadataPreviewEntrySurface`
- added `FHyperTwistTrainingCoachGeneratedModeSelectorOptionLaunchRequestMetadataPreviewSurface`
- `UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingCoachLibrary.h`
- `UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingCoachLibrary.cpp`
- added `DeriveCoachGeneratedModeSelectorOptionLaunchRequestMetadataPreviewSurface(...)`
- metadata preview is derived from cached run state plus the already-landed launch-request preview surface and imported runtime-surface metadata
- `UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingPanelWidget.h`
- `UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingPanelWidget.cpp`
- added `GetDisplayedCoachGeneratedModeSelectorOptionLaunchRequestMetadataPreviewSurface(const FString& SelectorId)`
- `UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingSessionActor.h`
- `UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingSessionActor.cpp`
- added `GetDisplayedCoachGeneratedModeSelectorOptionLaunchRequestMetadataPreviewSurface(const FString& SelectorId)`
Surface behavior:
- reports imported origin-path and notes provenance for each imported option on the chosen selector over the already-landed hypothetical payload posture
- keeps direct owned-case launch explicitly outside generated-mode launch-request metadata preview instead of pretending that generated-mode provenance still applies
- keeps unavailable options explicitly unavailable instead of forcing callers to infer metadata status from raw launch-request preview kinds
- reports a compact current-selection metadata preview for the chosen selector without requiring callers to read raw launch-config metadata arrays or notes strings
- keeps the surface presentation-only and compatible with the already-landed generated-mode launch / execution, request-alignment, stored-request delta, and payload / refresh-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 stored launch-request envelope / request-source preview surface so callers can render request id, request source, requested-at timestamp, and reuse-vs-refresh persistence context for a chosen option without reading raw launch-request envelope fields directly.