From 4ce2d0eaa98a5bed3d66dff4eb56186a3addcf10 Mon Sep 17 00:00:00 2001 From: axiomlogicnexus Date: Fri, 8 May 2026 05:13:31 +0200 Subject: [PATCH] Expose gameplay selector launch-request metadata preview surface --- .../HyperTwistTrainingCoachLibrary.cpp | 331 ++++++++++++++++++ .../HyperTwistTrainingPanelWidget.cpp | 19 + .../HyperTwistTrainingSessionActor.cpp | 19 + .../HyperTwistTrainingCoachLibrary.h | 10 + .../HyperTwistTrainingPanelWidget.h | 6 + .../HyperTwistTrainingSessionActor.h | 6 + .../HyperTwistTrainingTypes.h | 177 ++++++++++ ...RTWIST_ROADMAP_OVERHAUL_EXPANSION_GUIDE.md | 4 +- ...NOTES_PREVIEW_SURFACE_PACKET_2026-05-08.md | 103 ++++++ 9 files changed, 673 insertions(+), 2 deletions(-) create mode 100644 docs/arch/HYPERTWIST_PHASE4_GAMEPLAY_GENERATED_MODE_SELECTOR_OPTION_LAUNCH_REQUEST_METADATA_ORIGIN_NOTES_PREVIEW_SURFACE_PACKET_2026-05-08.md diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingCoachLibrary.cpp b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingCoachLibrary.cpp index d9c000d..280d75a 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingCoachLibrary.cpp +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingCoachLibrary.cpp @@ -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& OriginPaths + ) -> FString + { + if (OriginPaths.Num() == 0) + { + return TEXT("Origin paths: none"); + } + + const int32 PreviewCount = FMath::Min(2, OriginPaths.Num()); + TArray 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; +} diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingPanelWidget.cpp b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingPanelWidget.cpp index 697fa84..06d9d6b 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingPanelWidget.cpp +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingPanelWidget.cpp @@ -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 diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingSessionActor.cpp b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingSessionActor.cpp index 84d4ea8..5e62f6d 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingSessionActor.cpp +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingSessionActor.cpp @@ -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 ) diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingCoachLibrary.h b/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingCoachLibrary.h index bc22b53..5fad70d 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingCoachLibrary.h +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingCoachLibrary.h @@ -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 + ); }; diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingPanelWidget.h b/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingPanelWidget.h index bdae80e..0ccff7f 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingPanelWidget.h +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingPanelWidget.h @@ -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, diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingSessionActor.h b/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingSessionActor.h index 60f3be8..046fef5 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingSessionActor.h +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingSessionActor.h @@ -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); diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingTypes.h b/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingTypes.h index 1ffb6e8..746b88a 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingTypes.h +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingTypes.h @@ -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 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 + OptionEntries; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + TArray 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; + } +}; diff --git a/docs/HYPERTWIST_ROADMAP_OVERHAUL_EXPANSION_GUIDE.md b/docs/HYPERTWIST_ROADMAP_OVERHAUL_EXPANSION_GUIDE.md index 1baf8f9..f01c212 100644 --- a/docs/HYPERTWIST_ROADMAP_OVERHAUL_EXPANSION_GUIDE.md +++ b/docs/HYPERTWIST_ROADMAP_OVERHAUL_EXPANSION_GUIDE.md @@ -68,8 +68,8 @@ Current correction call from the source-exposed audit and current execution refr - the imported generated-mode gap is closed; request/config/selection plumbing and the bounded clean-room executor are already landed in first-party code - the latest landed bounded `Phase 4` packet closes the retained-idle comparison/outcome actionability gap at the end of the recognition-assisted coach-to-review continuity lane, and the final retained-surface confirmation pass now leaves that closure lane functionally complete - the retained idle widget surface now keeps retained comparison and decision history inspectable without continuing to advertise live launch or guidance-outcome actions once the coach-to-review loop has already collapsed to truthful closed-loop idle - - the latest landed bounded `Phase 4` packet stays on that same gameplay-facing consumer lane and adds one thin generated-mode selector option 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 diff --git a/docs/arch/HYPERTWIST_PHASE4_GAMEPLAY_GENERATED_MODE_SELECTOR_OPTION_LAUNCH_REQUEST_METADATA_ORIGIN_NOTES_PREVIEW_SURFACE_PACKET_2026-05-08.md b/docs/arch/HYPERTWIST_PHASE4_GAMEPLAY_GENERATED_MODE_SELECTOR_OPTION_LAUNCH_REQUEST_METADATA_ORIGIN_NOTES_PREVIEW_SURFACE_PACKET_2026-05-08.md new file mode 100644 index 0000000..50ffaeb --- /dev/null +++ b/docs/arch/HYPERTWIST_PHASE4_GAMEPLAY_GENERATED_MODE_SELECTOR_OPTION_LAUNCH_REQUEST_METADATA_ORIGIN_NOTES_PREVIEW_SURFACE_PACKET_2026-05-08.md @@ -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.