Expose gameplay generated-mode selector option detail surface

This commit is contained in:
axiomlogicnexus 2026-05-08 02:26:42 +02:00
parent 99a82a9676
commit a92cf4306f
9 changed files with 497 additions and 2 deletions

View file

@ -7169,3 +7169,241 @@ UHyperTwistTrainingCoachLibrary::DeriveCoachGeneratedModeSelectorRosterSurface(
return Surface;
}
FHyperTwistTrainingCoachGeneratedModeSelectorOptionMenuSurface
UHyperTwistTrainingCoachLibrary::DeriveCoachGeneratedModeSelectorOptionMenuSurface(
const FHyperTwistTrainingDeck& ActiveDeck,
const FHyperTwistTrainingImportedRuntimeSelectionState& ImportedRuntimeSelectionState,
const FString& SelectorId,
const FHyperTwistTrainingCoachGeneratedModeSelectorRosterSurface& SelectorRosterSurface
)
{
FHyperTwistTrainingCoachGeneratedModeSelectorOptionMenuSurface Surface;
Surface.SelectorId = SelectorId;
Surface.Headline = TEXT("Generated Mode Selector Options");
const FHyperTwistTrainingImportedRuntimeSurface& RuntimeSurface = ActiveDeck.ImportedRuntimeSurface;
const bool bHasGeneratedModeSurface = RuntimeSurface.IsStructurallyValid()
&& RuntimeSurface.SurfaceKind.Equals(TEXT("generated-mode"), ESearchCase::IgnoreCase);
if (SelectorId.IsEmpty())
{
Surface.StatusLine =
TEXT("Generated-mode selector option detail requires a selector id from the active selector roster.");
Surface.SelectionLine = TEXT("Selection: n/a");
Surface.AvailabilityLine = TEXT("Options: n/a");
Surface.DetailLine = SelectorRosterSurface.StatusLine.IsEmpty()
? TEXT("Roster: n/a")
: FString::Printf(TEXT("Roster: %s"), *SelectorRosterSurface.StatusLine);
return Surface;
}
const FHyperTwistTrainingCoachGeneratedModeSelectorRosterEntrySurface* MatchingRosterEntry =
SelectorRosterSurface.SelectorEntries.FindByPredicate(
[&SelectorId](const FHyperTwistTrainingCoachGeneratedModeSelectorRosterEntrySurface& Entry)
{
return Entry.SelectorId == SelectorId;
}
);
const FHyperTwistTrainingImportedRuntimeSelectorChoice* MatchingChoice =
ImportedRuntimeSelectionState.SelectorChoices.FindByPredicate(
[&SelectorId](const FHyperTwistTrainingImportedRuntimeSelectorChoice& Candidate)
{
return Candidate.SelectorId == SelectorId && Candidate.IsStructurallyValid();
}
);
const FHyperTwistTrainingImportedRuntimeSelector* MatchingSelector =
RuntimeSurface.Selectors.FindByPredicate(
[&SelectorId](const FHyperTwistTrainingImportedRuntimeSelector& Candidate)
{
return Candidate.SelectorId == SelectorId && Candidate.IsStructurallyValid();
}
);
Surface.bHasSelection = MatchingChoice != nullptr;
if (MatchingChoice != nullptr)
{
Surface.SelectedValue = MatchingChoice->SelectedValue;
Surface.SelectedDisplayValue = MatchingChoice->DisplayValue.IsEmpty()
? MatchingChoice->SelectedValue
: MatchingChoice->DisplayValue;
}
else if (MatchingRosterEntry != nullptr && MatchingRosterEntry->bHasSelection)
{
Surface.bHasSelection = true;
Surface.SelectedValue = MatchingRosterEntry->SelectedValue;
Surface.SelectedDisplayValue = MatchingRosterEntry->SelectedDisplayValue;
}
if (MatchingSelector != nullptr)
{
Surface.bHasSelector = true;
Surface.SelectorId = MatchingSelector->SelectorId;
Surface.SelectorLabel = MatchingSelector->Label;
Surface.BindingLabel = MatchingSelector->SelectorBinding;
Surface.Headline = MatchingSelector->Label.IsEmpty()
? FString::Printf(TEXT("Generated Mode Selector Options: %s"), *MatchingSelector->SelectorId)
: FString::Printf(TEXT("Generated Mode Selector Options: %s"), *MatchingSelector->Label);
for (const FHyperTwistTrainingImportedRuntimeSelectorOption& Option : MatchingSelector->Options)
{
if (!Option.IsStructurallyValid())
{
continue;
}
FHyperTwistTrainingCoachGeneratedModeSelectorOptionEntrySurface Entry;
Entry.OptionId = Option.OptionId;
Entry.Label = Option.Label;
Entry.OptionValue = Option.OptionValue;
Entry.DetailLine = Entry.OptionValue.IsEmpty() || Entry.OptionValue == Entry.OptionId
? FString::Printf(TEXT("Option id: %s"), *Entry.OptionId)
: FString::Printf(
TEXT("Option id: %s | Value: %s"),
*Entry.OptionId,
*Entry.OptionValue
);
Entry.bMatchesCurrentSelection = Surface.bHasSelection
&& (Surface.SelectedValue == Option.OptionId || Surface.SelectedValue == Option.OptionValue);
if (Entry.bMatchesCurrentSelection && !Surface.bSelectionMatchesAvailableOption)
{
Surface.bSelectionMatchesAvailableOption = true;
Surface.MatchedOptionId = Entry.OptionId;
Surface.MatchedOptionLabel = Entry.Label;
Surface.MatchedOptionValue = Entry.OptionValue;
}
Surface.OptionEntries.Add(MoveTemp(Entry));
}
}
Surface.AvailableOptionCount = Surface.OptionEntries.Num();
Surface.bHasOptions = Surface.AvailableOptionCount > 0;
Surface.bHasUnresolvedSelectionDetail =
Surface.bHasSelection && !Surface.bSelectionMatchesAvailableOption;
if (Surface.bHasSelection)
{
const FString SelectionSummary = Surface.SelectedDisplayValue.IsEmpty()
? Surface.SelectedValue
: Surface.SelectedDisplayValue;
Surface.SelectionLine = Surface.bSelectionMatchesAvailableOption
? FString::Printf(
TEXT("Selection: %s [%s]"),
SelectionSummary.IsEmpty() ? TEXT("n/a") : *SelectionSummary,
Surface.MatchedOptionId.IsEmpty() ? TEXT("n/a") : *Surface.MatchedOptionId
)
: FString::Printf(
TEXT("Selection: %s (cached)"),
SelectionSummary.IsEmpty() ? TEXT("n/a") : *SelectionSummary
);
}
else if (Surface.bHasSelector)
{
Surface.SelectionLine = TEXT("Selection: unresolved");
}
else
{
Surface.SelectionLine = TEXT("Selection: n/a");
}
if (Surface.bHasUnresolvedSelectionDetail)
{
const FString SelectionDisplaySummary = Surface.SelectedDisplayValue.IsEmpty()
? Surface.SelectedValue
: Surface.SelectedDisplayValue;
Surface.UnresolvedSelectionLine =
Surface.SelectedDisplayValue.IsEmpty() || Surface.SelectedDisplayValue == Surface.SelectedValue
? FString::Printf(
TEXT("Cached selection '%s' is not present in the imported options."),
SelectionDisplaySummary.IsEmpty() ? TEXT("n/a") : *SelectionDisplaySummary
)
: FString::Printf(
TEXT("Cached selection '%s' stored as '%s' is not present in the imported options."),
SelectionDisplaySummary.IsEmpty() ? TEXT("n/a") : *SelectionDisplaySummary,
Surface.SelectedValue.IsEmpty() ? TEXT("n/a") : *Surface.SelectedValue
);
}
if (Surface.bHasOptions)
{
Surface.AvailabilityLine = FString::Printf(
TEXT("Options: %d imported"),
Surface.AvailableOptionCount
);
}
else if (Surface.bHasSelector)
{
Surface.AvailabilityLine = TEXT("Options: no imported options");
}
else if (bHasGeneratedModeSurface)
{
Surface.AvailabilityLine = TEXT("Options: selector not found in active generated-mode surface");
}
else
{
Surface.AvailabilityLine = TEXT("Options: n/a");
}
if (Surface.bHasSelector && Surface.bHasOptions && Surface.bSelectionMatchesAvailableOption)
{
Surface.StatusLine = FString::Printf(
TEXT("Generated-mode selector option detail exposes %d imported option%s and the cached selection resolves cleanly."),
Surface.AvailableOptionCount,
Surface.AvailableOptionCount == 1 ? TEXT("") : TEXT("s")
);
}
else if (Surface.bHasSelector && Surface.bHasOptions && Surface.bHasSelection)
{
Surface.StatusLine = FString::Printf(
TEXT("Generated-mode selector option detail exposes %d imported option%s, but the cached selection no longer matches an imported option."),
Surface.AvailableOptionCount,
Surface.AvailableOptionCount == 1 ? TEXT("") : TEXT("s")
);
}
else if (Surface.bHasSelector && Surface.bHasOptions)
{
Surface.StatusLine = FString::Printf(
TEXT("Generated-mode selector option detail exposes %d imported option%s and the selector is currently unresolved."),
Surface.AvailableOptionCount,
Surface.AvailableOptionCount == 1 ? TEXT("") : TEXT("s")
);
}
else if (Surface.bHasSelector)
{
Surface.StatusLine =
TEXT("Generated-mode selector option detail is active, but this selector does not expose imported options.");
}
else if (bHasGeneratedModeSurface)
{
Surface.StatusLine =
TEXT("Generated-mode selector option detail could not find the requested selector in the active generated-mode surface.");
}
else if (SelectorRosterSurface.bHasSelectorRoster || ImportedRuntimeSelectionState.IsStructurallyValid())
{
Surface.StatusLine =
TEXT("Generated-mode selector option detail is not active, but cached selector state remains.");
}
else
{
Surface.StatusLine =
TEXT("Generated-mode selector option detail is not currently available.");
}
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("Roster: %s | Selector: %s | Binding: %s"),
SelectorRosterSurface.StatusLine.IsEmpty()
? TEXT("n/a")
: *SelectorRosterSurface.StatusLine,
*SelectorSummary,
*BindingSummary
);
return Surface;
}

View file

@ -704,6 +704,21 @@ UHyperTwistTrainingPanelWidget::GetDisplayedCoachGeneratedModeSelectorRosterSurf
);
}
FHyperTwistTrainingCoachGeneratedModeSelectorOptionMenuSurface
UHyperTwistTrainingPanelWidget::GetDisplayedCoachGeneratedModeSelectorOptionMenuSurface(
const FString& SelectorId
) const
{
const FHyperTwistTrainingCoachGeneratedModeSelectorRosterSurface SelectorRosterSurface =
GetDisplayedCoachGeneratedModeSelectorRosterSurface();
return UHyperTwistTrainingCoachLibrary::DeriveCoachGeneratedModeSelectorOptionMenuSurface(
CachedRunState.ActiveDeck,
CachedImportedRuntimeSelectionState,
SelectorId,
SelectorRosterSurface
);
}
FHyperTwistTrainingRunState UHyperTwistTrainingPanelWidget::StartCoachRecommendedRun(
const int32 MaxCases,
const FString& StartActionSourceLabel

View file

@ -685,6 +685,21 @@ AHyperTwistTrainingSessionActor::GetDisplayedCoachGeneratedModeSelectorRosterSur
);
}
FHyperTwistTrainingCoachGeneratedModeSelectorOptionMenuSurface
AHyperTwistTrainingSessionActor::GetDisplayedCoachGeneratedModeSelectorOptionMenuSurface(
const FString& SelectorId
) const
{
const FHyperTwistTrainingCoachGeneratedModeSelectorRosterSurface SelectorRosterSurface =
GetDisplayedCoachGeneratedModeSelectorRosterSurface();
return UHyperTwistTrainingCoachLibrary::DeriveCoachGeneratedModeSelectorOptionMenuSurface(
CachedRunState.ActiveDeck,
CachedImportedRuntimeSelectionState,
SelectorId,
SelectorRosterSurface
);
}
FHyperTwistTrainingRunState AHyperTwistTrainingSessionActor::StartCoachRecommendedTrainingRun(
const int32 MaxCases
)

View file

@ -687,4 +687,13 @@ public:
const FHyperTwistTrainingCoachGeneratedModeSelectorApplicationContextSurface&
SelectorApplicationContextSurface
);
UFUNCTION(BlueprintPure, Category = "HyperTwist|Training|Coach")
static FHyperTwistTrainingCoachGeneratedModeSelectorOptionMenuSurface
DeriveCoachGeneratedModeSelectorOptionMenuSurface(
const FHyperTwistTrainingDeck& ActiveDeck,
const FHyperTwistTrainingImportedRuntimeSelectionState& ImportedRuntimeSelectionState,
const FString& SelectorId,
const FHyperTwistTrainingCoachGeneratedModeSelectorRosterSurface& SelectorRosterSurface
);
};

View file

@ -256,6 +256,10 @@ public:
FHyperTwistTrainingCoachGeneratedModeSelectorRosterSurface
GetDisplayedCoachGeneratedModeSelectorRosterSurface() const;
UFUNCTION(BlueprintPure, Category = "HyperTwist|Training|Coach")
FHyperTwistTrainingCoachGeneratedModeSelectorOptionMenuSurface
GetDisplayedCoachGeneratedModeSelectorOptionMenuSurface(const FString& SelectorId) const;
UFUNCTION(BlueprintCallable, Category = "HyperTwist|Training|Coach")
FHyperTwistTrainingRunState StartCoachRecommendedRun(
int32 MaxCases,

View file

@ -237,6 +237,10 @@ public:
FHyperTwistTrainingCoachGeneratedModeSelectorRosterSurface
GetDisplayedCoachGeneratedModeSelectorRosterSurface() const;
UFUNCTION(BlueprintPure, Category = "HyperTwist|Training|Coach")
FHyperTwistTrainingCoachGeneratedModeSelectorOptionMenuSurface
GetDisplayedCoachGeneratedModeSelectorOptionMenuSurface(const FString& SelectorId) const;
UFUNCTION(BlueprintCallable, Category = "HyperTwist|Training|Coach")
FHyperTwistTrainingRunState StartCoachRecommendedTrainingRun(int32 MaxCases);

View file

@ -8569,3 +8569,110 @@ struct FHyperTwistTrainingCoachGeneratedModeSelectorRosterSurface
|| bHasUnresolvedSelectors;
}
};
USTRUCT(BlueprintType)
struct FHyperTwistTrainingCoachGeneratedModeSelectorOptionEntrySurface
{
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")
FString DetailLine;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
bool bMatchesCurrentSelection = false;
bool IsStructurallyValid() const
{
return !OptionId.IsEmpty()
|| !Label.IsEmpty()
|| !OptionValue.IsEmpty()
|| bMatchesCurrentSelection;
}
};
USTRUCT(BlueprintType)
struct FHyperTwistTrainingCoachGeneratedModeSelectorOptionMenuSurface
{
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 SelectionLine;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString UnresolvedSelectionLine;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString AvailabilityLine;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString DetailLine;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
TArray<FHyperTwistTrainingCoachGeneratedModeSelectorOptionEntrySurface> OptionEntries;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString SelectedValue;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString SelectedDisplayValue;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString MatchedOptionId;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString MatchedOptionLabel;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString MatchedOptionValue;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
int32 AvailableOptionCount = 0;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
bool bHasSelector = false;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
bool bHasSelection = false;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
bool bHasOptions = false;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
bool bSelectionMatchesAvailableOption = false;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
bool bHasUnresolvedSelectionDetail = false;
bool IsStructurallyValid() const
{
return !StatusLine.IsEmpty()
|| !SelectorId.IsEmpty()
|| OptionEntries.Num() > 0
|| bHasSelector
|| bHasSelection;
}
};

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 roster / option-availability surface over the active imported runtime surface and cached selection state, so gameplay/Blueprint callers can render active selector labels, bindings, selected values, and unresolved option availability without reading raw imported selector arrays or option payloads directly
- the current next bounded packet is to expose one thin generated-mode selector option-menu / detail surface on those same two consumer surfaces so gameplay/Blueprint callers can render normalized selector-option ids, labels, and unresolved-selection detail for a chosen selector without reading raw imported option payloads directly
- the latest landed bounded `Phase 4` packet stays on that same gameplay-facing consumer lane and adds one thin parameterized generated-mode selector option-menu / detail surface over the already-cached selector roster/runtime state, so gameplay/Blueprint callers can render normalized selector-option ids, labels, values, and unresolved-selection detail for a chosen selector without reading raw imported option payloads directly
- the current next bounded packet is to expose one thin generated-mode selector option actionability / case-coverage surface on those same two consumer surfaces so gameplay/Blueprint callers can render which imported options still map cleanly to owned cases and which remain selection-only choices without reading raw option-case payloads or runtime probes 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-menu / detail 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 roster by exposing one parameterized selector option-menu / detail surface for a chosen selector id.
The open tasks were:
- stop forcing gameplay / Blueprint callers to read raw imported selector-option payloads just to render normalized option ids, labels, values, and chosen-selector option menus
- expose when a cached generated-mode selector choice still matches an imported option and when it has drifted out of the imported option list
- keep the surface derived from already-cached gameplay consumer selector/runtime state instead of widening into selector-application behavior or new runtime fetches
It is not:
- a new selector-application behavior packet
- a per-option actionability or case-coverage packet
- a generated-mode execution packet
- a broader gameplay UI composition pass
## Scope
Bounded lane:
- add first-party generated-mode selector option-entry and option-menu surfaces in shared training types
- add a coach-library helper that derives the chosen-selector option menu from the active deck imported runtime surface, cached imported selection state, requested selector id, and the already-landed selector roster surface
- expose that parameterized option-menu surface on `HyperTwistTrainingPanelWidget` and `HyperTwistTrainingSessionActor`
- keep the derivation presentation-only and rooted in already-cached gameplay consumer state
Out of scope:
- changing selector-choice persistence
- changing generated-mode launch-config derivation rules
- changing selector-option application behavior
- widening into per-option case coverage or actionability yet
## Why this was the right next packet
Before this slice:
- gameplay-facing panel/session consumers already exposed truthful generated-mode launch / execution context
- those same consumers already exposed truthful selector / application context
- those same consumers already exposed truthful selector roster / option-availability summary
But one thin consumer seam was still open:
- callers could list active selectors and see whether unresolved selectors still had imported options
- but they still had to read raw option payloads to render the option menu for a chosen selector and to explain whether the cached selection still matched a live imported option
That meant:
- selector roster posture was thin and reusable
- but chosen-selector option detail still leaked raw imported option payloads into callers
So the next bounded move was:
- keep the already-landed launch / execution, selector / application, and selector roster surfaces unchanged
- and expose one narrow parameterized option-menu / detail surface above that cached selector state
## What landed
Primary code changes:
- `UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingTypes.h`
- added `FHyperTwistTrainingCoachGeneratedModeSelectorOptionEntrySurface`
- added `FHyperTwistTrainingCoachGeneratedModeSelectorOptionMenuSurface`
- `UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingCoachLibrary.h`
- `UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingCoachLibrary.cpp`
- added `DeriveCoachGeneratedModeSelectorOptionMenuSurface(...)`
- option detail is derived from the active imported runtime surface, cached imported selection state, requested selector id, and the already-landed selector roster surface
- `UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingPanelWidget.h`
- `UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingPanelWidget.cpp`
- added `GetDisplayedCoachGeneratedModeSelectorOptionMenuSurface(const FString& SelectorId)`
- `UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingSessionActor.h`
- `UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingSessionActor.cpp`
- added `GetDisplayedCoachGeneratedModeSelectorOptionMenuSurface(const FString& SelectorId)`
Surface behavior:
- reports normalized option ids, labels, and option values for the chosen selector
- reports whether the cached selection still matches one of the current imported options
- reports unresolved-selection detail when cached selection state remains but no longer maps to the imported option list
- keeps the surface presentation-only and compatible with the already-landed generated-mode context and roster 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 actionability / case-coverage surface so callers can render which imported options still map to owned cases and which remain selection-only choices without reading raw option-case payloads or runtime probes directly.