From edf106da6ce124130b4c785728e03e44ee7c1545 Mon Sep 17 00:00:00 2001 From: axiomlogicnexus Date: Fri, 8 May 2026 16:15:59 +0200 Subject: [PATCH] Expose gameplay selector owned-execution provenance preview surface --- .../HyperTwistTrainingCoachLibrary.cpp | 822 ++++++++++++++++++ .../HyperTwistTrainingPanelWidget.cpp | 19 + .../HyperTwistTrainingSessionActor.cpp | 19 + .../HyperTwistTrainingCoachLibrary.h | 10 + .../HyperTwistTrainingPanelWidget.h | 6 + .../HyperTwistTrainingSessionActor.h | 6 + .../HyperTwistTrainingTypes.h | 243 ++++++ ...RTWIST_ROADMAP_OVERHAUL_EXPANSION_GUIDE.md | 4 +- ...NANCE_PREVIEW_SURFACE_PACKET_2026-05-08.md | 102 +++ 9 files changed, 1229 insertions(+), 2 deletions(-) create mode 100644 docs/arch/HYPERTWIST_PHASE4_GAMEPLAY_GENERATED_MODE_SELECTOR_OPTION_OWNED_EXECUTION_SOURCE_ATTRIBUTION_TAG_PROVENANCE_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 af5c8f6..b6a053f 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingCoachLibrary.cpp +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingCoachLibrary.cpp @@ -5654,6 +5654,828 @@ FHyperTwistTrainingCoachPrimaryActionSurface UHyperTwistTrainingCoachLibrary::De return Surface; } +FHyperTwistTrainingCoachGeneratedModeSelectorOptionOwnedExecutionProvenancePreviewSurface +UHyperTwistTrainingCoachLibrary::DeriveCoachGeneratedModeSelectorOptionOwnedExecutionProvenancePreviewSurface( + const FHyperTwistTrainingRunState& RunState, + const FString& SelectorId, + const FHyperTwistTrainingCoachGeneratedModeSelectorOptionMenuSurface& OptionMenuSurface, + const FHyperTwistTrainingCoachGeneratedModeSelectorOptionLaunchRequestPreviewSurface& + OptionLaunchRequestPreviewSurface +) +{ + FHyperTwistTrainingCoachGeneratedModeSelectorOptionOwnedExecutionProvenancePreviewSurface + Surface; + Surface.SelectorId = SelectorId; + Surface.SelectorLabel = OptionMenuSurface.SelectorLabel; + Surface.BindingLabel = OptionMenuSurface.BindingLabel; + Surface.Headline = + TEXT("Generated Mode Selector Option Owned-Execution Provenance Preview"); + Surface.bHasSelector = OptionMenuSurface.bHasSelector; + + if (!SelectorId.IsEmpty()) + { + Surface.Headline = OptionMenuSurface.SelectorLabel.IsEmpty() + ? FString::Printf( + TEXT("Generated Mode Selector Option Owned-Execution Provenance Preview: %s"), + *SelectorId + ) + : FString::Printf( + TEXT("Generated Mode Selector Option Owned-Execution Provenance Preview: %s"), + *OptionMenuSurface.SelectorLabel + ); + } + + if (SelectorId.IsEmpty()) + { + Surface.StatusLine = + TEXT("Generated-mode selector option owned-execution provenance preview requires a selector id from the active selector roster."); + Surface.CaseSourceSummaryLine = TEXT("Case source attribution: n/a"); + Surface.DeckSourceSummaryLine = TEXT("Deck source attribution: n/a"); + Surface.TagSummaryLine = TEXT("Tag posture: n/a"); + Surface.CurrentSelectionProvenanceLine = 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 FHyperTwistTrainingImportedGeneratedModeLaunchRequest& LaunchRequest = + RunState.ImportedGeneratedModeLaunchRequest; + 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 SanitizeGeneratedModeToken = [](const FString& Value) -> FString + { + FString Sanitized; + Sanitized.Reserve(Value.Len()); + + for (const TCHAR Character : Value) + { + if (FChar::IsAlnum(Character)) + { + Sanitized.AppendChar(FChar::ToLower(Character)); + } + else if (Sanitized.IsEmpty() || Sanitized[Sanitized.Len() - 1] != TEXT('-')) + { + Sanitized.AppendChar(TEXT('-')); + } + } + + while (Sanitized.StartsWith(TEXT("-"))) + { + Sanitized.RightChopInline(1, EAllowShrinking::No); + } + while (Sanitized.EndsWith(TEXT("-"))) + { + Sanitized.LeftChopInline(1, EAllowShrinking::No); + } + + return Sanitized.IsEmpty() ? TEXT("value") : Sanitized; + }; + + const auto DescribeSourceMode = [](const EHyperTwistTrainingSourceMode SourceMode) -> FString + { + switch (SourceMode) + { + case EHyperTwistTrainingSourceMode::FirstParty: + return TEXT("First Party"); + case EHyperTwistTrainingSourceMode::DirectDonor: + return TEXT("Direct Donor"); + case EHyperTwistTrainingSourceMode::CleanRoom: + return TEXT("Clean Room"); + case EHyperTwistTrainingSourceMode::Benchmark: + return TEXT("Benchmark"); + default: + return TEXT("Unknown"); + } + }; + + const auto BuildSourceModeLine = [&DescribeSourceMode]( + const TCHAR* Prefix, + const FHyperTwistTrainingSourceAttribution& Attribution + ) -> FString + { + return FString::Printf( + TEXT("%s: %s"), + Prefix, + *DescribeSourceMode(Attribution.SourceMode) + ); + }; + + const auto BuildSourceRefLine = []( + const TCHAR* Prefix, + const FHyperTwistTrainingSourceAttribution& Attribution + ) -> FString + { + TArray Parts; + if (!Attribution.SourceRepo.IsEmpty()) + { + Parts.Add(FString::Printf(TEXT("repo=%s"), *Attribution.SourceRepo)); + } + if (!Attribution.SourceLicense.IsEmpty()) + { + Parts.Add(FString::Printf(TEXT("license=%s"), *Attribution.SourceLicense)); + } + if (!Attribution.SourceTrackId.IsEmpty()) + { + Parts.Add(FString::Printf(TEXT("track=%s"), *Attribution.SourceTrackId)); + } + if (!Attribution.SourceDeckId.IsEmpty()) + { + Parts.Add(FString::Printf(TEXT("deck=%s"), *Attribution.SourceDeckId)); + } + if (!Attribution.SourceCaseId.IsEmpty()) + { + Parts.Add(FString::Printf(TEXT("case=%s"), *Attribution.SourceCaseId)); + } + + if (Parts.Num() == 0) + { + return FString::Printf(TEXT("%s: first-party lineage"), Prefix); + } + + return FString::Printf( + TEXT("%s: %s"), + Prefix, + *FString::Join(Parts, TEXT(" | ")) + ); + }; + + const auto BuildSourceNotesLine = []( + const TCHAR* Prefix, + const FHyperTwistTrainingSourceAttribution& Attribution + ) -> FString + { + return FString::Printf( + TEXT("%s: %s"), + Prefix, + Attribution.SourceNotes.IsEmpty() ? TEXT("none") : *Attribution.SourceNotes + ); + }; + + const auto BuildTagSummaryLine = []( + const TCHAR* Prefix, + const TArray& Tags + ) -> FString + { + if (Tags.Num() == 0) + { + return FString::Printf(TEXT("%s: none"), Prefix); + } + + TArray PreviewTags; + const int32 PreviewLimit = FMath::Min(3, Tags.Num()); + for (int32 Index = 0; Index < PreviewLimit; ++Index) + { + PreviewTags.Add(Tags[Index]); + } + + FString PreviewText = FString::Join(PreviewTags, TEXT(" | ")); + if (Tags.Num() > PreviewLimit) + { + PreviewText += FString::Printf( + TEXT(" | +%d more"), + Tags.Num() - PreviewLimit + ); + } + + return FString::Printf( + TEXT("%s: %d [%s]"), + Prefix, + Tags.Num(), + *PreviewText + ); + }; + + const auto AppendUniqueTag = []( + TArray& Tags, + const FString& Tag + ) + { + if (!Tag.IsEmpty()) + { + Tags.AddUnique(Tag); + } + }; + + const auto IncrementSourceModeCounts = [&Surface]( + const FHyperTwistTrainingSourceAttribution& Attribution, + const bool bCaseSource + ) + { + switch (Attribution.SourceMode) + { + case EHyperTwistTrainingSourceMode::FirstParty: + if (bCaseSource) + { + ++Surface.FirstPartyCaseSourceOptionCount; + } + else + { + ++Surface.FirstPartyDeckSourceOptionCount; + } + break; + case EHyperTwistTrainingSourceMode::DirectDonor: + if (bCaseSource) + { + ++Surface.DirectDonorCaseSourceOptionCount; + } + else + { + ++Surface.DirectDonorDeckSourceOptionCount; + } + break; + case EHyperTwistTrainingSourceMode::CleanRoom: + if (bCaseSource) + { + ++Surface.CleanRoomCaseSourceOptionCount; + } + else + { + ++Surface.CleanRoomDeckSourceOptionCount; + } + break; + case EHyperTwistTrainingSourceMode::Benchmark: + if (bCaseSource) + { + ++Surface.BenchmarkCaseSourceOptionCount; + } + else + { + ++Surface.BenchmarkDeckSourceOptionCount; + } + break; + default: + break; + } + }; + + if (!RunState.IsStructurallyValid() || !bHasGeneratedModeSurface) + { + Surface.StatusLine = OptionMenuSurface.StatusLine.IsEmpty() + ? TEXT("Generated-mode selector option owned-execution provenance preview is not currently available.") + : OptionMenuSurface.StatusLine; + Surface.CaseSourceSummaryLine = TEXT("Case source attribution: n/a"); + Surface.DeckSourceSummaryLine = TEXT("Deck source attribution: n/a"); + Surface.TagSummaryLine = TEXT("Tag posture: n/a"); + Surface.CurrentSelectionProvenanceLine = OptionMenuSurface.bHasUnresolvedSelectionDetail + ? OptionMenuSurface.UnresolvedSelectionLine + : (OptionMenuSurface.bHasSelection + ? TEXT("Current selection remains cached, but no active owned-execution provenance 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) + { + FHyperTwistTrainingCoachGeneratedModeSelectorOptionOwnedExecutionProvenancePreviewEntrySurface + 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.PreviewCaseSourceAttribution = MatchingCase.SourceAttribution; + Entry.PreviewDeckSourceAttribution = RunState.ActiveDeck.SourceAttribution; + Entry.PreviewCaseSourceModeLabel = DescribeSourceMode( + Entry.PreviewCaseSourceAttribution.SourceMode + ); + Entry.PreviewDeckSourceModeLabel = DescribeSourceMode( + Entry.PreviewDeckSourceAttribution.SourceMode + ); + Entry.PreviewCaseTags = MatchingCase.Tags; + Entry.PreviewDeckTags = RunState.ActiveDeck.Tags; + AppendUniqueTag( + Entry.PreviewDeckTags, + TEXT("runtime-surface:selector-option-applied") + ); + AppendUniqueTag( + Entry.PreviewDeckTags, + FString::Printf(TEXT("selector:%s"), *SelectorId.ToLower()) + ); + AppendUniqueTag( + Entry.PreviewDeckTags, + FString::Printf(TEXT("selector-option:%s"), *PreviewEntry.OptionId.ToLower()) + ); + + Entry.bHasCaseSourceAttribution = + Entry.PreviewCaseSourceAttribution.IsStructurallyValid(); + Entry.bHasDeckSourceAttribution = + Entry.PreviewDeckSourceAttribution.IsStructurallyValid(); + Entry.CaseTagCount = Entry.PreviewCaseTags.Num(); + Entry.DeckTagCount = Entry.PreviewDeckTags.Num(); + Entry.bHasCaseTags = Entry.CaseTagCount > 0; + Entry.bHasDeckTags = Entry.DeckTagCount > 0; + Entry.CaseSourceModeLine = BuildSourceModeLine( + TEXT("Case source mode"), + Entry.PreviewCaseSourceAttribution + ); + Entry.CaseSourceRefLine = BuildSourceRefLine( + TEXT("Case source ref"), + Entry.PreviewCaseSourceAttribution + ); + Entry.CaseSourceNotesLine = BuildSourceNotesLine( + TEXT("Case source notes"), + Entry.PreviewCaseSourceAttribution + ); + Entry.DeckSourceModeLine = BuildSourceModeLine( + TEXT("Deck source mode"), + Entry.PreviewDeckSourceAttribution + ); + Entry.DeckSourceRefLine = BuildSourceRefLine( + TEXT("Deck source ref"), + Entry.PreviewDeckSourceAttribution + ); + Entry.DeckSourceNotesLine = BuildSourceNotesLine( + TEXT("Deck source notes"), + Entry.PreviewDeckSourceAttribution + ); + Entry.CaseTagSummaryLine = BuildTagSummaryLine( + TEXT("Case tags on execute"), + Entry.PreviewCaseTags + ); + Entry.DeckTagSummaryLine = BuildTagSummaryLine( + TEXT("Deck tags on execute"), + Entry.PreviewDeckTags + ); + Entry.ProvenanceSourceLine = + TEXT("Provenance source: exact direct covered-case attribution from the resolved imported option case plus active deck lineage."); + Entry.PreviewStabilityLine = + TEXT("Preview stability: exact direct owned-case provenance preview from covered-case launch."); + Entry.bHasExactProvenancePreview = true; + Entry.bCanPreview = true; + ++Surface.DirectExactOptionCount; + IncrementSourceModeCounts(Entry.PreviewCaseSourceAttribution, true); + IncrementSourceModeCounts(Entry.PreviewDeckSourceAttribution, false); + } + else + { + Entry.CaseSourceModeLine = TEXT("Case source mode: unavailable"); + Entry.CaseSourceRefLine = TEXT("Case source ref: unavailable"); + Entry.CaseSourceNotesLine = TEXT("Case source notes: unavailable"); + Entry.DeckSourceModeLine = TEXT("Deck source mode: unavailable"); + Entry.DeckSourceRefLine = TEXT("Deck source ref: unavailable"); + Entry.DeckSourceNotesLine = TEXT("Deck source notes: unavailable"); + Entry.CaseTagSummaryLine = TEXT("Case tags on execute: unavailable"); + Entry.DeckTagSummaryLine = TEXT("Deck tags on execute: unavailable"); + Entry.ProvenanceSourceLine = + TEXT("Provenance 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.CaseSourceModeLine = TEXT("Case source mode: unavailable"); + Entry.CaseSourceRefLine = TEXT("Case source ref: unavailable"); + Entry.CaseSourceNotesLine = TEXT("Case source notes: unavailable"); + Entry.DeckSourceModeLine = TEXT("Deck source mode: unavailable"); + Entry.DeckSourceRefLine = TEXT("Deck source ref: unavailable"); + Entry.DeckSourceNotesLine = TEXT("Deck source notes: unavailable"); + Entry.CaseTagSummaryLine = TEXT("Case tags on execute: unavailable"); + Entry.DeckTagSummaryLine = TEXT("Deck tags on execute: unavailable"); + Entry.ProvenanceSourceLine = + TEXT("Provenance 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; + } + + const bool bStoredRequestReusable = + PreviewEntry.LaunchRequestPreviewKind + == EHyperTwistTrainingCoachGeneratedModeOptionLaunchRequestPreviewKind:: + StoredRequestReusable; + const bool bHasExactRequestId = + bStoredRequestReusable && LaunchRequest.IsStructurallyValid() + && !LaunchRequest.RequestId.IsEmpty(); + const FString PreviewRequestId = bHasExactRequestId + ? LaunchRequest.RequestId + : FString(TEXT("")); + const FString PreviewRequestTagToken = bHasExactRequestId + ? SanitizeGeneratedModeToken(PreviewRequestId) + : FString(TEXT("")); + const FString PreviewSurfaceToken = PreviewEntry.HypotheticalSurfaceId.IsEmpty() + ? SanitizeGeneratedModeToken(RuntimeSurface.SurfaceId) + : SanitizeGeneratedModeToken(PreviewEntry.HypotheticalSurfaceId); + const FString CaseExecutionNote = FString::Printf( + TEXT("Owned generated-mode execution case produced from request %s."), + *PreviewRequestId + ); + const FString DeckExecutionNote = FString::Printf( + TEXT("Owned generated-mode execution deck produced from request %s."), + *PreviewRequestId + ); + + Entry.PreviewCaseSourceAttribution = + SeedCase->SourceAttribution.IsStructurallyValid() + ? SeedCase->SourceAttribution + : RunState.ActiveDeck.SourceAttribution; + if (Entry.PreviewCaseSourceAttribution.SourceCaseId.IsEmpty()) + { + Entry.PreviewCaseSourceAttribution.SourceCaseId = SeedCase->CaseId; + } + Entry.PreviewCaseSourceAttribution.SourceNotes = + Entry.PreviewCaseSourceAttribution.SourceNotes.IsEmpty() + ? CaseExecutionNote + : FString::Printf( + TEXT("%s | %s"), + *Entry.PreviewCaseSourceAttribution.SourceNotes, + *CaseExecutionNote + ); + Entry.PreviewDeckSourceAttribution = RunState.ActiveDeck.SourceAttribution; + Entry.PreviewDeckSourceAttribution.SourceNotes = + Entry.PreviewDeckSourceAttribution.SourceNotes.IsEmpty() + ? DeckExecutionNote + : FString::Printf( + TEXT("%s | %s"), + *Entry.PreviewDeckSourceAttribution.SourceNotes, + *DeckExecutionNote + ); + Entry.PreviewCaseSourceModeLabel = DescribeSourceMode( + Entry.PreviewCaseSourceAttribution.SourceMode + ); + Entry.PreviewDeckSourceModeLabel = DescribeSourceMode( + Entry.PreviewDeckSourceAttribution.SourceMode + ); + Entry.PreviewCaseTags = SeedCase->Tags; + AppendUniqueTag( + Entry.PreviewCaseTags, + TEXT("runtime-surface:generated-mode") + ); + AppendUniqueTag( + Entry.PreviewCaseTags, + TEXT("generated-mode:owned-execution") + ); + AppendUniqueTag( + Entry.PreviewCaseTags, + FString::Printf( + TEXT("generated-request:%s"), + *PreviewRequestTagToken + ) + ); + AppendUniqueTag( + Entry.PreviewCaseTags, + FString::Printf( + TEXT("generated-surface:%s"), + *PreviewSurfaceToken + ) + ); + for (const FHyperTwistTrainingCoachGeneratedModeSelectorOptionLaunchRequestSelectorChoiceEntrySurface& + ChoiceEntry : PreviewEntry.SelectorChoiceEntries) + { + if (!ChoiceEntry.bHasChoice || ChoiceEntry.SelectorId.IsEmpty() + || ChoiceEntry.SelectedValue.IsEmpty()) + { + continue; + } + + AppendUniqueTag( + Entry.PreviewCaseTags, + FString::Printf( + TEXT("generated-selector:%s=%s"), + *SanitizeGeneratedModeToken(ChoiceEntry.SelectorId), + *SanitizeGeneratedModeToken(ChoiceEntry.SelectedValue) + ) + ); + } + + Entry.PreviewDeckTags = RunState.ActiveDeck.Tags; + AppendUniqueTag( + Entry.PreviewDeckTags, + TEXT("runtime-surface:generated-mode") + ); + AppendUniqueTag( + Entry.PreviewDeckTags, + TEXT("generated-mode:owned-execution") + ); + AppendUniqueTag( + Entry.PreviewDeckTags, + FString::Printf( + TEXT("generated-request:%s"), + *PreviewRequestTagToken + ) + ); + + Entry.bHasCaseSourceAttribution = + Entry.PreviewCaseSourceAttribution.IsStructurallyValid(); + Entry.bHasDeckSourceAttribution = + Entry.PreviewDeckSourceAttribution.IsStructurallyValid(); + Entry.CaseTagCount = Entry.PreviewCaseTags.Num(); + Entry.DeckTagCount = Entry.PreviewDeckTags.Num(); + Entry.bHasCaseTags = Entry.CaseTagCount > 0; + Entry.bHasDeckTags = Entry.DeckTagCount > 0; + Entry.CaseSourceModeLine = BuildSourceModeLine( + TEXT("Case source mode"), + Entry.PreviewCaseSourceAttribution + ); + Entry.CaseSourceRefLine = BuildSourceRefLine( + TEXT("Case source ref"), + Entry.PreviewCaseSourceAttribution + ); + Entry.CaseSourceNotesLine = BuildSourceNotesLine( + TEXT("Case source notes"), + Entry.PreviewCaseSourceAttribution + ); + Entry.DeckSourceModeLine = BuildSourceModeLine( + TEXT("Deck source mode"), + Entry.PreviewDeckSourceAttribution + ); + Entry.DeckSourceRefLine = BuildSourceRefLine( + TEXT("Deck source ref"), + Entry.PreviewDeckSourceAttribution + ); + Entry.DeckSourceNotesLine = BuildSourceNotesLine( + TEXT("Deck source notes"), + Entry.PreviewDeckSourceAttribution + ); + Entry.CaseTagSummaryLine = BuildTagSummaryLine( + TEXT("Case tags on execute"), + Entry.PreviewCaseTags + ); + Entry.DeckTagSummaryLine = BuildTagSummaryLine( + TEXT("Deck tags on execute"), + Entry.PreviewDeckTags + ); + Entry.ProvenanceSourceLine = + bHasExactRequestId + ? TEXT("Provenance source: generated owned-execution attribution and tags synthesized exactly from the seed case, active deck lineage, and reusable stored request.") + : TEXT("Provenance source: generated owned-execution attribution is exact, but request-id-dependent notes and tags remain preview templates until execute refreshes the launch request."); + Entry.PreviewStabilityLine = + bHasExactRequestId + ? TEXT("Preview stability: exact while execute reuses the current stored request.") + : TEXT("Preview stability: case/deck attribution is exact, but request-id-specific notes and tags remain template preview until launch-request refresh."); + Entry.bHasExactProvenancePreview = bHasExactRequestId; + Entry.bCanPreview = true; + ++Surface.GeneratedExactOptionCount; + IncrementSourceModeCounts(Entry.PreviewCaseSourceAttribution, true); + IncrementSourceModeCounts(Entry.PreviewDeckSourceAttribution, false); + break; + } + + case EHyperTwistTrainingCoachGeneratedModeOptionLaunchRequestPreviewKind:: + UnresolvedAfterChoice: + Entry.CaseSourceModeLine = + TEXT("Case source mode: blocked until every required selector is resolved."); + Entry.CaseSourceRefLine = + TEXT("Case source ref: blocked until every required selector is resolved."); + Entry.CaseSourceNotesLine = + TEXT("Case source notes: blocked until every required selector is resolved."); + Entry.DeckSourceModeLine = + TEXT("Deck source mode: blocked until every required selector is resolved."); + Entry.DeckSourceRefLine = + TEXT("Deck source ref: blocked until every required selector is resolved."); + Entry.DeckSourceNotesLine = + TEXT("Deck source notes: blocked until every required selector is resolved."); + Entry.CaseTagSummaryLine = + TEXT("Case tags on execute: blocked until every required selector is resolved."); + Entry.DeckTagSummaryLine = + TEXT("Deck tags on execute: blocked until every required selector is resolved."); + Entry.ProvenanceSourceLine = + TEXT("Provenance 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.CaseSourceModeLine = + TEXT("Case source mode: blocked until a structurally valid launch payload can be derived."); + Entry.CaseSourceRefLine = + TEXT("Case source ref: blocked until a structurally valid launch payload can be derived."); + Entry.CaseSourceNotesLine = + TEXT("Case source notes: blocked until a structurally valid launch payload can be derived."); + Entry.DeckSourceModeLine = + TEXT("Deck source mode: blocked until a structurally valid launch payload can be derived."); + Entry.DeckSourceRefLine = + TEXT("Deck source ref: blocked until a structurally valid launch payload can be derived."); + Entry.DeckSourceNotesLine = + TEXT("Deck source notes: blocked until a structurally valid launch payload can be derived."); + Entry.CaseTagSummaryLine = + TEXT("Case tags on execute: blocked until a structurally valid launch payload can be derived."); + Entry.DeckTagSummaryLine = + TEXT("Deck tags on execute: blocked until a structurally valid launch payload can be derived."); + Entry.ProvenanceSourceLine = + TEXT("Provenance 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.CaseSourceModeLine = TEXT("Case source mode: unavailable"); + Entry.CaseSourceRefLine = TEXT("Case source ref: unavailable"); + Entry.CaseSourceNotesLine = TEXT("Case source notes: unavailable"); + Entry.DeckSourceModeLine = TEXT("Deck source mode: unavailable"); + Entry.DeckSourceRefLine = TEXT("Deck source ref: unavailable"); + Entry.DeckSourceNotesLine = TEXT("Deck source notes: unavailable"); + Entry.CaseTagSummaryLine = TEXT("Case tags on execute: unavailable"); + Entry.DeckTagSummaryLine = TEXT("Deck tags on execute: unavailable"); + Entry.ProvenanceSourceLine = TEXT("Provenance source: unavailable"); + Entry.PreviewStabilityLine = TEXT("Preview stability: unavailable"); + Entry.bCanPreview = false; + ++Surface.UnavailableOptionCount; + break; + } + + if (Entry.bHasCaseTags) + { + ++Surface.CaseTaggedOptionCount; + } + if (Entry.bHasDeckTags) + { + ++Surface.DeckTaggedOptionCount; + } + if (Entry.PreviewCaseTags.Contains(TEXT("generated-mode:owned-execution"))) + { + ++Surface.GeneratedExecutionCaseTagOptionCount; + } + if (Entry.PreviewDeckTags.Contains(TEXT("generated-mode:owned-execution"))) + { + ++Surface.GeneratedExecutionDeckTagOptionCount; + } + + Entry.DetailLine = PreviewEntry.DetailLine.IsEmpty() + ? Entry.PreviewStabilityLine + : FString::Printf( + TEXT("%s | %s"), + *Entry.PreviewStabilityLine, + *PreviewEntry.DetailLine + ); + + if (Entry.bMatchesCurrentSelection) + { + Surface.bCurrentSelectionHasExactProvenancePreview = + Entry.bHasExactProvenancePreview; + Surface.bCurrentSelectionHasCaseSourceAttribution = + Entry.bHasCaseSourceAttribution; + Surface.bCurrentSelectionHasDeckSourceAttribution = + Entry.bHasDeckSourceAttribution; + Surface.bCurrentSelectionHasCaseTags = Entry.bHasCaseTags; + Surface.bCurrentSelectionHasDeckTags = Entry.bHasDeckTags; + Surface.CurrentSelectionProvenanceLine = FString::Printf( + TEXT("Current selection: case %s | deck %s | case tags %d | deck tags %d | %s"), + Entry.PreviewCaseSourceModeLabel.IsEmpty() + ? TEXT("n/a") + : *Entry.PreviewCaseSourceModeLabel, + Entry.PreviewDeckSourceModeLabel.IsEmpty() + ? TEXT("n/a") + : *Entry.PreviewDeckSourceModeLabel, + Entry.CaseTagCount, + Entry.DeckTagCount, + *Entry.PreviewStabilityLine + ); + } + + Surface.OptionEntries.Add(MoveTemp(Entry)); + } + + Surface.bHasOwnedExecutionProvenancePreviewOptions = Surface.OptionEntries.Num() > 0; + Surface.bHasDirectProvenancePreviewOptions = Surface.DirectExactOptionCount > 0; + Surface.bHasGeneratedProvenancePreviewOptions = Surface.GeneratedExactOptionCount > 0; + Surface.bHasBlockedProvenancePreviewOptions = Surface.BlockedOptionCount > 0; + + Surface.CaseSourceSummaryLine = Surface.OptionEntries.Num() > 0 + ? FString::Printf( + TEXT("Case source attribution: %d first-party | %d direct donor | %d clean-room | %d benchmark"), + Surface.FirstPartyCaseSourceOptionCount, + Surface.DirectDonorCaseSourceOptionCount, + Surface.CleanRoomCaseSourceOptionCount, + Surface.BenchmarkCaseSourceOptionCount + ) + : TEXT("Case source attribution: no imported options"); + Surface.DeckSourceSummaryLine = Surface.OptionEntries.Num() > 0 + ? FString::Printf( + TEXT("Deck source attribution: %d first-party | %d direct donor | %d clean-room | %d benchmark"), + Surface.FirstPartyDeckSourceOptionCount, + Surface.DirectDonorDeckSourceOptionCount, + Surface.CleanRoomDeckSourceOptionCount, + Surface.BenchmarkDeckSourceOptionCount + ) + : TEXT("Deck source attribution: no imported options"); + Surface.TagSummaryLine = Surface.OptionEntries.Num() > 0 + ? FString::Printf( + TEXT("Tag posture: %d case-tagged | %d deck-tagged | %d generated-case-tagged | %d generated-deck-tagged"), + Surface.CaseTaggedOptionCount, + Surface.DeckTaggedOptionCount, + Surface.GeneratedExecutionCaseTagOptionCount, + Surface.GeneratedExecutionDeckTagOptionCount + ) + : TEXT("Tag posture: no imported options"); + + if (Surface.CurrentSelectionProvenanceLine.IsEmpty()) + { + if (OptionMenuSurface.bHasUnresolvedSelectionDetail) + { + Surface.CurrentSelectionProvenanceLine = + OptionMenuSurface.UnresolvedSelectionLine; + } + else if (OptionMenuSurface.bHasSelection) + { + Surface.CurrentSelectionProvenanceLine = + TEXT("Current selection has no additional owned-execution provenance preview."); + } + else if (OptionMenuSurface.bHasSelector) + { + Surface.CurrentSelectionProvenanceLine = TEXT("Current selection: unresolved"); + } + else + { + Surface.CurrentSelectionProvenanceLine = TEXT("Current selection: n/a"); + } + } + + if (OptionMenuSurface.bHasSelector && Surface.OptionEntries.Num() > 0) + { + Surface.StatusLine = FString::Printf( + TEXT("Generated-mode selector option owned-execution provenance preview exposes %d direct exact option%s, %d generated preview option%s, %d blocked option%s, and %d unavailable option%s."), + Surface.DirectExactOptionCount, + Surface.DirectExactOptionCount == 1 ? TEXT("") : TEXT("s"), + Surface.GeneratedExactOptionCount, + Surface.GeneratedExactOptionCount == 1 ? TEXT("") : TEXT("s"), + Surface.BlockedOptionCount, + Surface.BlockedOptionCount == 1 ? TEXT("") : TEXT("s"), + Surface.UnavailableOptionCount, + Surface.UnavailableOptionCount == 1 ? TEXT("") : TEXT("s") + ); + } + else if (OptionMenuSurface.bHasSelector) + { + Surface.StatusLine = + TEXT("Generated-mode selector option owned-execution provenance preview is active, but this selector does not expose imported options."); + } + else + { + Surface.StatusLine = OptionMenuSurface.StatusLine.IsEmpty() + ? TEXT("Generated-mode selector option owned-execution provenance 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; +} + FHyperTwistTrainingCoachQueueControlSurface UHyperTwistTrainingCoachLibrary::DeriveCoachQueueControlSurface( const FHyperTwistTrainingCoachSessionQueueState& QueueState, diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingPanelWidget.cpp b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingPanelWidget.cpp index 3f0ce96..43ef274 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingPanelWidget.cpp +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingPanelWidget.cpp @@ -948,6 +948,25 @@ UHyperTwistTrainingPanelWidget::GetDisplayedCoachGeneratedModeSelectorOptionOwne ); } +FHyperTwistTrainingCoachGeneratedModeSelectorOptionOwnedExecutionProvenancePreviewSurface +UHyperTwistTrainingPanelWidget::GetDisplayedCoachGeneratedModeSelectorOptionOwnedExecutionProvenancePreviewSurface( + const FString& SelectorId +) const +{ + const FHyperTwistTrainingCoachGeneratedModeSelectorOptionMenuSurface OptionMenuSurface = + GetDisplayedCoachGeneratedModeSelectorOptionMenuSurface(SelectorId); + const FHyperTwistTrainingCoachGeneratedModeSelectorOptionLaunchRequestPreviewSurface + OptionLaunchRequestPreviewSurface = + GetDisplayedCoachGeneratedModeSelectorOptionLaunchRequestPreviewSurface(SelectorId); + return UHyperTwistTrainingCoachLibrary:: + DeriveCoachGeneratedModeSelectorOptionOwnedExecutionProvenancePreviewSurface( + 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 2547548..28d28a2 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingSessionActor.cpp +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingSessionActor.cpp @@ -929,6 +929,25 @@ AHyperTwistTrainingSessionActor::GetDisplayedCoachGeneratedModeSelectorOptionOwn ); } +FHyperTwistTrainingCoachGeneratedModeSelectorOptionOwnedExecutionProvenancePreviewSurface +AHyperTwistTrainingSessionActor::GetDisplayedCoachGeneratedModeSelectorOptionOwnedExecutionProvenancePreviewSurface( + const FString& SelectorId +) const +{ + const FHyperTwistTrainingCoachGeneratedModeSelectorOptionMenuSurface OptionMenuSurface = + GetDisplayedCoachGeneratedModeSelectorOptionMenuSurface(SelectorId); + const FHyperTwistTrainingCoachGeneratedModeSelectorOptionLaunchRequestPreviewSurface + OptionLaunchRequestPreviewSurface = + GetDisplayedCoachGeneratedModeSelectorOptionLaunchRequestPreviewSurface(SelectorId); + return UHyperTwistTrainingCoachLibrary:: + DeriveCoachGeneratedModeSelectorOptionOwnedExecutionProvenancePreviewSurface( + 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 92d9c50..fccecaf 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingCoachLibrary.h +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingCoachLibrary.h @@ -817,4 +817,14 @@ public: const FHyperTwistTrainingCoachGeneratedModeSelectorOptionLaunchRequestPreviewSurface& OptionLaunchRequestPreviewSurface ); + + UFUNCTION(BlueprintPure, Category = "HyperTwist|Training|Coach") + static FHyperTwistTrainingCoachGeneratedModeSelectorOptionOwnedExecutionProvenancePreviewSurface + DeriveCoachGeneratedModeSelectorOptionOwnedExecutionProvenancePreviewSurface( + 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 316a2d5..18adf61 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingPanelWidget.h +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingPanelWidget.h @@ -320,6 +320,12 @@ public: const FString& SelectorId ) const; + UFUNCTION(BlueprintPure, Category = "HyperTwist|Training|Coach") + FHyperTwistTrainingCoachGeneratedModeSelectorOptionOwnedExecutionProvenancePreviewSurface + GetDisplayedCoachGeneratedModeSelectorOptionOwnedExecutionProvenancePreviewSurface( + 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 b2be63e..9a08940 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingSessionActor.h +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingSessionActor.h @@ -301,6 +301,12 @@ public: const FString& SelectorId ) const; + UFUNCTION(BlueprintPure, Category = "HyperTwist|Training|Coach") + FHyperTwistTrainingCoachGeneratedModeSelectorOptionOwnedExecutionProvenancePreviewSurface + GetDisplayedCoachGeneratedModeSelectorOptionOwnedExecutionProvenancePreviewSurface( + 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 b0f0db2..1374c4f 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingTypes.h +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingTypes.h @@ -10882,3 +10882,246 @@ struct FHyperTwistTrainingCoachGeneratedModeSelectorOptionOwnedExecutionPuzzleSu || bHasOwnedExecutionPuzzleSubsetDifficultyPreviewOptions; } }; + +USTRUCT(BlueprintType) +struct FHyperTwistTrainingCoachGeneratedModeSelectorOptionOwnedExecutionProvenancePreviewEntrySurface +{ + 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 CaseSourceModeLine; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString CaseSourceRefLine; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString CaseSourceNotesLine; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString DeckSourceModeLine; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString DeckSourceRefLine; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString DeckSourceNotesLine; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString CaseTagSummaryLine; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString DeckTagSummaryLine; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString ProvenanceSourceLine; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString PreviewStabilityLine; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString DetailLine; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FHyperTwistTrainingSourceAttribution PreviewCaseSourceAttribution; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FHyperTwistTrainingSourceAttribution PreviewDeckSourceAttribution; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString PreviewCaseSourceModeLabel; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString PreviewDeckSourceModeLabel; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + TArray PreviewCaseTags; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + TArray PreviewDeckTags; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + int32 CaseTagCount = 0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + int32 DeckTagCount = 0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bMatchesCurrentSelection = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bStartsOwnedCaseImmediately = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bHasExactProvenancePreview = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bHasCaseSourceAttribution = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bHasDeckSourceAttribution = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bHasCaseTags = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bHasDeckTags = 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() + || !CaseSourceModeLine.IsEmpty() + || !DeckSourceModeLine.IsEmpty() + || !CaseTagSummaryLine.IsEmpty() + || !DeckTagSummaryLine.IsEmpty() + || LaunchRequestPreviewKind + != EHyperTwistTrainingCoachGeneratedModeOptionLaunchRequestPreviewKind::None + || bCanPreview; + } +}; + +USTRUCT(BlueprintType) +struct FHyperTwistTrainingCoachGeneratedModeSelectorOptionOwnedExecutionProvenancePreviewSurface +{ + 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 CaseSourceSummaryLine; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString DeckSourceSummaryLine; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString TagSummaryLine; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString CurrentSelectionProvenanceLine; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString DetailLine; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + TArray + OptionEntries; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + int32 DirectExactOptionCount = 0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + int32 GeneratedExactOptionCount = 0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + int32 BlockedOptionCount = 0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + int32 UnavailableOptionCount = 0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + int32 FirstPartyCaseSourceOptionCount = 0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + int32 DirectDonorCaseSourceOptionCount = 0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + int32 CleanRoomCaseSourceOptionCount = 0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + int32 BenchmarkCaseSourceOptionCount = 0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + int32 FirstPartyDeckSourceOptionCount = 0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + int32 DirectDonorDeckSourceOptionCount = 0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + int32 CleanRoomDeckSourceOptionCount = 0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + int32 BenchmarkDeckSourceOptionCount = 0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + int32 CaseTaggedOptionCount = 0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + int32 DeckTaggedOptionCount = 0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + int32 GeneratedExecutionCaseTagOptionCount = 0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + int32 GeneratedExecutionDeckTagOptionCount = 0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bHasSelector = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bHasOwnedExecutionProvenancePreviewOptions = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bHasDirectProvenancePreviewOptions = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bHasGeneratedProvenancePreviewOptions = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bHasBlockedProvenancePreviewOptions = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bCurrentSelectionHasExactProvenancePreview = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bCurrentSelectionHasCaseSourceAttribution = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bCurrentSelectionHasDeckSourceAttribution = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bCurrentSelectionHasCaseTags = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bCurrentSelectionHasDeckTags = false; + + bool IsStructurallyValid() const + { + return !StatusLine.IsEmpty() + || !SelectorId.IsEmpty() + || OptionEntries.Num() > 0 + || bHasSelector + || bHasOwnedExecutionProvenancePreviewOptions; + } +}; diff --git a/docs/HYPERTWIST_ROADMAP_OVERHAUL_EXPANSION_GUIDE.md b/docs/HYPERTWIST_ROADMAP_OVERHAUL_EXPANSION_GUIDE.md index 8ba29e5..4752d16 100644 --- a/docs/HYPERTWIST_ROADMAP_OVERHAUL_EXPANSION_GUIDE.md +++ b/docs/HYPERTWIST_ROADMAP_OVERHAUL_EXPANSION_GUIDE.md @@ -68,8 +68,8 @@ Current correction call from the source-exposed audit and current execution refr - the imported generated-mode gap is closed; request/config/selection plumbing and the bounded clean-room executor are already landed in first-party code - the latest landed bounded `Phase 4` packet closes the retained-idle comparison/outcome actionability gap at the end of the recognition-assisted coach-to-review continuity lane, and the final retained-surface confirmation pass now leaves that closure lane functionally complete - the retained idle widget surface now keeps retained comparison and decision history inspectable without continuing to advertise live launch or guidance-outcome actions once the coach-to-review loop has already collapsed to truthful closed-loop idle - - the latest landed bounded `Phase 4` packet stays on that same gameplay-facing consumer lane and adds one thin generated-mode selector option owned execution puzzle-id / subset-id / difficulty preview surface over the already-cached selector/runtime/request state, so gameplay/Blueprint callers can render exact direct-case scope and exact generated owned execution puzzle, subset, and difficulty posture 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 source-attribution / tag provenance preview surface on those same two consumer surfaces so gameplay/Blueprint callers can render the resulting owned execution provenance and tag posture 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 source-attribution / tag provenance preview surface over the already-cached selector/runtime/request state, so gameplay/Blueprint callers can render resulting case/deck attribution plus exact-or-template tag posture 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 deck-title / selection-policy preview surface on those same two consumer surfaces so gameplay/Blueprint callers can render resulting owned execution container identity and selection-policy 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 diff --git a/docs/arch/HYPERTWIST_PHASE4_GAMEPLAY_GENERATED_MODE_SELECTOR_OPTION_OWNED_EXECUTION_SOURCE_ATTRIBUTION_TAG_PROVENANCE_PREVIEW_SURFACE_PACKET_2026-05-08.md b/docs/arch/HYPERTWIST_PHASE4_GAMEPLAY_GENERATED_MODE_SELECTOR_OPTION_OWNED_EXECUTION_SOURCE_ATTRIBUTION_TAG_PROVENANCE_PREVIEW_SURFACE_PACKET_2026-05-08.md new file mode 100644 index 0000000..9752a92 --- /dev/null +++ b/docs/arch/HYPERTWIST_PHASE4_GAMEPLAY_GENERATED_MODE_SELECTOR_OPTION_OWNED_EXECUTION_SOURCE_ATTRIBUTION_TAG_PROVENANCE_PREVIEW_SURFACE_PACKET_2026-05-08.md @@ -0,0 +1,102 @@ +# HyperTwist Phase 4 gameplay generated-mode selector option owned execution source-attribution / tag provenance preview surface packet + +Created on `2026-05-08` + +Status: + +- first-party HyperTwist packet +- bounded Phase `4` gameplay-facing consumer slice + +## Purpose + +This packet closes the next thin gameplay-facing consumer seam above the panel-widget and session-actor generated-mode selector option owned execution puzzle-id / subset-id / difficulty preview surface by exposing one parameterized owned execution provenance preview surface for a chosen selector id. + +The open tasks were: + +- stop forcing gameplay / Blueprint callers to rebuild generated deck attribution rules and tag synthesis just to explain where a chosen imported option would execute from +- expose direct covered-case case/deck attribution plus generated owned-execution case/deck attribution on the same thin consumer lane +- expose resulting case and deck tag posture, including generated request/surface/selector tags, without widening into backend execution behavior + +It is not: + +- a selector-dispatch behavior change +- a generated-mode launch-request persistence behavior change +- a generated-mode execution deck synthesis change +- a broader gameplay UI composition pass + +## Scope + +Bounded lane: + +- add first-party owned execution provenance preview surfaces in shared training types +- add a coach-library helper that projects case/deck source attribution and tag posture over the already-landed launch-request preview surface plus active run state +- expose that parameterized provenance preview surface on `HyperTwistTrainingPanelWidget` and `HyperTwistTrainingSessionActor` +- keep the derivation presentation-only and rooted in the already-owned selector/runtime/request surfaces + +Out of scope: + +- changing selector-choice apply behavior +- changing stored launch-request creation, refresh, or execute behavior +- changing generated-mode execution deck synthesis +- widening into dashboard-only or broader gameplay composition work + +## Why this was the right next packet + +Before this slice: + +- gameplay-facing panel/session consumers already exposed truthful owned case identity, prompt presentation, delivery mode, reveal posture, target time, and execution scope preview for imported selector options +- callers could tell what owned execution would run + +But one thin consumer seam was still open: + +- callers still had to reconstruct source attribution and generated tag posture from direct covered-case fields, active deck state, stored request state, and generated owned-execution synthesis rules if they wanted to explain where that execution came from + +That meant: + +- execution preview was already thin and reusable +- but provenance and tag posture still leaked generated deck composition rules into callers + +So the next bounded move was: + +- keep the already-landed owned execution scope preview surface unchanged in behavior +- reuse the already-cached launch-request preview surface and active run state to expose one narrow owned execution provenance surface above it + +## What landed + +Primary code changes: + +- `UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingTypes.h` + - added `FHyperTwistTrainingCoachGeneratedModeSelectorOptionOwnedExecutionProvenancePreviewEntrySurface` + - added `FHyperTwistTrainingCoachGeneratedModeSelectorOptionOwnedExecutionProvenancePreviewSurface` +- `UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingCoachLibrary.h` +- `UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingCoachLibrary.cpp` + - added `DeriveCoachGeneratedModeSelectorOptionOwnedExecutionProvenancePreviewSurface(...)` + - provenance 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 `GetDisplayedCoachGeneratedModeSelectorOptionOwnedExecutionProvenancePreviewSurface(const FString& SelectorId)` +- `UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingSessionActor.h` +- `UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingSessionActor.cpp` + - added `GetDisplayedCoachGeneratedModeSelectorOptionOwnedExecutionProvenancePreviewSurface(const FString& SelectorId)` + +Surface behavior: + +- reports exact direct covered-case case/deck attribution and direct execution tag posture for selector options that dispatch straight to owned cases +- reports generated owned-execution case/deck attribution and generated execution tag posture for previewable generated-mode options +- keeps reusable stored-request cases exact, while marking refresh-required cases as attribution-exact but request-id-dependent note/tag templates until execute refreshes the launch request +- keeps unresolved or payload-invalid choices inspectable as blocked provenance-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, owned execution case / canonical-state preview, owned execution prompt preview, owned execution delivery preview, and owned execution scope 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 deck-title / selection-policy preview surface so callers can render resulting owned execution container identity and selection-policy posture for a chosen option without rebuilding generated deck composition rules directly.