Expose gameplay focus provenance surface

This commit is contained in:
axiomlogicnexus 2026-05-08 00:04:45 +02:00
parent 0d813df46b
commit 53bb7ab349
9 changed files with 474 additions and 2 deletions

View file

@ -3,6 +3,7 @@
namespace HyperTwistTrainingCoachLibraryInternal
{
EHyperTwistCoachPriority PriorityFromScore(const float Score);
FString DescribeCoachMemorySourceLabel(const FString& SourceLabel);
EHyperTwistTrainingCoachGuidanceLane GuidanceLaneFromSourceLabel(const FString& SourceLabel)
{
@ -352,6 +353,48 @@ namespace HyperTwistTrainingCoachLibraryInternal
}
}
FString DescribeCoachFocusTargetLabel(
const FString& FocusDeckId,
const FString& FocusMethodSegmentId)
{
if (!FocusDeckId.IsEmpty() && !FocusMethodSegmentId.IsEmpty())
{
return FString::Printf(
TEXT("deck %s | method %s"),
*FocusDeckId,
*FocusMethodSegmentId
);
}
if (!FocusDeckId.IsEmpty())
{
return FString::Printf(TEXT("deck %s"), *FocusDeckId);
}
if (!FocusMethodSegmentId.IsEmpty())
{
return FString::Printf(TEXT("method %s"), *FocusMethodSegmentId);
}
return TEXT("no focused coach target");
}
FString DescribeGameplayCoachSourceLabel(const FString& SourceLabel)
{
if (SourceLabel.IsEmpty())
{
return TEXT("n/a");
}
if (SourceLabel.StartsWith(TEXT("coach-panel-")))
{
return TEXT("panel-surface coach action routing");
}
if (SourceLabel.StartsWith(TEXT("coach-session-actor-")))
{
return TEXT("session-actor coach action routing");
}
return DescribeCoachMemorySourceLabel(SourceLabel);
}
FString DescribePrimaryCoachActionLabel(
const FHyperTwistTrainingCoachPanelState& PanelState,
const EHyperTwistTrainingCoachHandoffKind ActionKind
@ -5968,3 +6011,161 @@ UHyperTwistTrainingCoachLibrary::DeriveCoachHandoffContinuationSurface(
return Surface;
}
FHyperTwistTrainingCoachFocusProvenanceSurface
UHyperTwistTrainingCoachLibrary::DeriveCoachFocusProvenanceSurface(
const FHyperTwistTrainingCoachPanelState& PanelState,
const FHyperTwistTrainingCoachPrimaryActionSurface& PrimaryActionSurface,
const FHyperTwistTrainingCoachQueueControlSurface& QueueControlSurface,
const FHyperTwistTrainingCoachQueueRecoveryPostureSurface& QueueRecoveryPostureSurface,
const FHyperTwistTrainingCoachHandoffContinuationSurface& HandoffContinuationSurface
)
{
FHyperTwistTrainingCoachFocusProvenanceSurface Surface;
Surface.Headline = TEXT("Coach Focus");
Surface.FocusHeadline = !PrimaryActionSurface.Headline.IsEmpty()
? PrimaryActionSurface.Headline
: PanelState.Headline;
Surface.FocusDeckId = !PrimaryActionSurface.FocusDeckId.IsEmpty()
? PrimaryActionSurface.FocusDeckId
: PanelState.FocusDeckId;
Surface.FocusMethodSegmentId = !PrimaryActionSurface.FocusMethodSegmentId.IsEmpty()
? PrimaryActionSurface.FocusMethodSegmentId
: PanelState.FocusMethodSegmentId;
Surface.ContinuationKind = HandoffContinuationSurface.ContinuationKind;
Surface.QueuePostureKind = QueueRecoveryPostureSurface.PostureKind;
Surface.PreferredGuidanceLane = PanelState.PreferredGuidanceLane;
Surface.bHasFocusTarget =
!Surface.FocusDeckId.IsEmpty() || !Surface.FocusMethodSegmentId.IsEmpty();
if (HandoffContinuationSurface.bQueueRecoveryOverridesContinuation)
{
if (QueueRecoveryPostureSurface.PostureKind
== EHyperTwistTrainingCoachQueuePostureKind::Promote)
{
Surface.ActiveActionLabel = QueueControlSurface.PromoteActionLabel;
Surface.ActiveSourceLabel = !QueueControlSurface.PromoteActionSourceLabel.IsEmpty()
? QueueControlSurface.PromoteActionSourceLabel
: (!QueueRecoveryPostureSurface.PreferredActionSourceLabel.IsEmpty()
? QueueRecoveryPostureSurface.PreferredActionSourceLabel
: PanelState.PreferredQueueRecoveryActionSourceLabel);
}
else if (QueueRecoveryPostureSurface.PostureKind
== EHyperTwistTrainingCoachQueuePostureKind::Defer)
{
Surface.ActiveActionLabel = QueueControlSurface.DeferActionLabel;
Surface.ActiveSourceLabel = !QueueRecoveryPostureSurface.PreferredActionSourceLabel.IsEmpty()
? QueueRecoveryPostureSurface.PreferredActionSourceLabel
: (!PanelState.PreferredQueueRecoveryActionSourceLabel.IsEmpty()
? PanelState.PreferredQueueRecoveryActionSourceLabel
: PanelState.PreferredCoachHandoffSourceLabel);
}
}
else if (HandoffContinuationSurface.bHasActionableContinuation)
{
Surface.ActiveActionLabel = HandoffContinuationSurface.ContinuationActionLabel;
Surface.ActiveSourceLabel = !HandoffContinuationSurface.ContinuationSourceLabel.IsEmpty()
? HandoffContinuationSurface.ContinuationSourceLabel
: PanelState.PreferredCoachHandoffSourceLabel;
}
else if (QueueRecoveryPostureSurface.bCanExecutePreferredAction)
{
Surface.ActiveActionLabel = QueueRecoveryPostureSurface.PreferredActionLabel;
Surface.ActiveSourceLabel = !QueueRecoveryPostureSurface.PreferredActionSourceLabel.IsEmpty()
? QueueRecoveryPostureSurface.PreferredActionSourceLabel
: PanelState.PreferredQueueRecoveryActionSourceLabel;
}
Surface.ActiveSourceDescription =
HyperTwistTrainingCoachLibraryInternal::DescribeGameplayCoachSourceLabel(
Surface.ActiveSourceLabel
);
Surface.GuidanceSourceDescription =
HyperTwistTrainingCoachLibraryInternal::DescribeGameplayCoachSourceLabel(
PanelState.PreferredGuidanceSourceLabel
);
Surface.HandoffSourceDescription =
HyperTwistTrainingCoachLibraryInternal::DescribeGameplayCoachSourceLabel(
PanelState.PreferredCoachHandoffSourceLabel
);
Surface.QueueSourceDescription =
HyperTwistTrainingCoachLibraryInternal::DescribeGameplayCoachSourceLabel(
PanelState.NextQueueSourceLabel
);
Surface.RecoverySourceDescription =
HyperTwistTrainingCoachLibraryInternal::DescribeGameplayCoachSourceLabel(
PanelState.PreferredQueueRecoveryActionSourceLabel
);
Surface.LaunchBudgetSourceDescription =
HyperTwistTrainingCoachLibraryInternal::DescribeGameplayCoachSourceLabel(
PanelState.PreferredLaunchBudgetSourceLabel
);
Surface.bHasUpstreamProvenance =
!Surface.ActiveSourceLabel.IsEmpty()
|| !PanelState.PreferredGuidanceSourceLabel.IsEmpty()
|| !PanelState.PreferredCoachHandoffSourceLabel.IsEmpty()
|| !PanelState.NextQueueSourceLabel.IsEmpty()
|| !PanelState.PreferredQueueRecoveryActionSourceLabel.IsEmpty()
|| !PanelState.PreferredLaunchBudgetSourceLabel.IsEmpty();
const FString FocusTargetLabel = HyperTwistTrainingCoachLibraryInternal::DescribeCoachFocusTargetLabel(
Surface.FocusDeckId,
Surface.FocusMethodSegmentId
);
const FString ContinuationKindLabel =
HyperTwistTrainingCoachLibraryInternal::DescribeCoachHandoffKind(
Surface.ContinuationKind
);
const FString QueuePostureLabel =
HyperTwistTrainingCoachLibraryInternal::DescribeCoachQueuePostureKind(
Surface.QueuePostureKind
);
const FString GuidanceLaneLabel = HyperTwistTrainingCoachLibraryInternal::DescribeGuidanceLane(
Surface.PreferredGuidanceLane
);
Surface.FocusSummaryLine = FString::Printf(
TEXT("Focus target: %s | Active action: %s | Continuation: %s"),
*FocusTargetLabel,
Surface.ActiveActionLabel.IsEmpty() ? TEXT("none") : *Surface.ActiveActionLabel,
*ContinuationKindLabel
);
if (!Surface.FocusHeadline.IsEmpty())
{
Surface.FocusSummaryLine += FString::Printf(
TEXT(" | Headline: %s"),
*Surface.FocusHeadline
);
}
Surface.ProvenanceSummaryLine = FString::Printf(
TEXT("Active provenance: %s | Guidance: %s | Handoff: %s | Recovery: %s"),
*Surface.ActiveSourceDescription,
*Surface.GuidanceSourceDescription,
*Surface.HandoffSourceDescription,
*Surface.RecoverySourceDescription
);
Surface.DetailLine = FString::Printf(
TEXT("Queue posture: %s | Preferred lane: %s | Queue source: %s | Launch budget source: %s | Handoff status: %s | Queue recovery: %s"),
*QueuePostureLabel,
*GuidanceLaneLabel,
*Surface.QueueSourceDescription,
*Surface.LaunchBudgetSourceDescription,
HandoffContinuationSurface.StatusLine.IsEmpty()
? TEXT("n/a")
: *HandoffContinuationSurface.StatusLine,
QueueRecoveryPostureSurface.StatusLine.IsEmpty()
? TEXT("n/a")
: *QueueRecoveryPostureSurface.StatusLine
);
if (!HandoffContinuationSurface.DetailLine.IsEmpty())
{
Surface.DetailLine += FString::Printf(
TEXT(" | Continuation detail: %s"),
*HandoffContinuationSurface.DetailLine
);
}
return Surface;
}

View file

@ -490,6 +490,37 @@ UHyperTwistTrainingPanelWidget::GetDisplayedCoachHandoffContinuationSurface() co
);
}
FHyperTwistTrainingCoachFocusProvenanceSurface
UHyperTwistTrainingPanelWidget::GetDisplayedCoachFocusProvenanceSurface() const
{
const FHyperTwistTrainingCoachPrimaryActionSurface PrimaryActionSurface =
GetDisplayedPrimaryCoachActionSurface();
const FHyperTwistTrainingCoachQueueControlSurface QueueControlSurface =
GetDisplayedCoachQueueControlSurface();
const FHyperTwistTrainingCoachQueueRecoveryPostureSurface QueueRecoveryPostureSurface =
UHyperTwistTrainingCoachLibrary::DeriveCoachQueueRecoveryPostureSurface(
CachedCoachSchedulePolicySummary,
CachedCoachQueueExecutionSummary,
CachedCoachPanelState,
PrimaryActionSurface,
QueueControlSurface
);
const FHyperTwistTrainingCoachHandoffContinuationSurface HandoffContinuationSurface =
UHyperTwistTrainingCoachLibrary::DeriveCoachHandoffContinuationSurface(
CachedCoachPanelState,
PrimaryActionSurface,
QueueControlSurface,
QueueRecoveryPostureSurface
);
return UHyperTwistTrainingCoachLibrary::DeriveCoachFocusProvenanceSurface(
CachedCoachPanelState,
PrimaryActionSurface,
QueueControlSurface,
QueueRecoveryPostureSurface,
HandoffContinuationSurface
);
}
FHyperTwistTrainingRunState UHyperTwistTrainingPanelWidget::StartCoachRecommendedRun(
const int32 MaxCases,
const FString& StartActionSourceLabel

View file

@ -471,6 +471,37 @@ AHyperTwistTrainingSessionActor::GetDisplayedCoachHandoffContinuationSurface() c
);
}
FHyperTwistTrainingCoachFocusProvenanceSurface
AHyperTwistTrainingSessionActor::GetDisplayedCoachFocusProvenanceSurface() const
{
const FHyperTwistTrainingCoachPrimaryActionSurface PrimaryActionSurface =
GetDisplayedPrimaryCoachActionSurface();
const FHyperTwistTrainingCoachQueueControlSurface QueueControlSurface =
GetDisplayedCoachQueueControlSurface();
const FHyperTwistTrainingCoachQueueRecoveryPostureSurface QueueRecoveryPostureSurface =
UHyperTwistTrainingCoachLibrary::DeriveCoachQueueRecoveryPostureSurface(
CachedCoachSchedulePolicySummary,
CachedCoachQueueExecutionSummary,
CachedCoachPanelState,
PrimaryActionSurface,
QueueControlSurface
);
const FHyperTwistTrainingCoachHandoffContinuationSurface HandoffContinuationSurface =
UHyperTwistTrainingCoachLibrary::DeriveCoachHandoffContinuationSurface(
CachedCoachPanelState,
PrimaryActionSurface,
QueueControlSurface,
QueueRecoveryPostureSurface
);
return UHyperTwistTrainingCoachLibrary::DeriveCoachFocusProvenanceSurface(
CachedCoachPanelState,
PrimaryActionSurface,
QueueControlSurface,
QueueRecoveryPostureSurface,
HandoffContinuationSurface
);
}
FHyperTwistTrainingRunState AHyperTwistTrainingSessionActor::StartCoachRecommendedTrainingRun(
const int32 MaxCases
)

View file

@ -619,4 +619,14 @@ public:
const FHyperTwistTrainingCoachQueueControlSurface& QueueControlSurface,
const FHyperTwistTrainingCoachQueueRecoveryPostureSurface& QueueRecoveryPostureSurface
);
UFUNCTION(BlueprintPure, Category = "HyperTwist|Training|Coach")
static FHyperTwistTrainingCoachFocusProvenanceSurface
DeriveCoachFocusProvenanceSurface(
const FHyperTwistTrainingCoachPanelState& PanelState,
const FHyperTwistTrainingCoachPrimaryActionSurface& PrimaryActionSurface,
const FHyperTwistTrainingCoachQueueControlSurface& QueueControlSurface,
const FHyperTwistTrainingCoachQueueRecoveryPostureSurface& QueueRecoveryPostureSurface,
const FHyperTwistTrainingCoachHandoffContinuationSurface& HandoffContinuationSurface
);
};

View file

@ -228,6 +228,10 @@ public:
FHyperTwistTrainingCoachHandoffContinuationSurface
GetDisplayedCoachHandoffContinuationSurface() const;
UFUNCTION(BlueprintPure, Category = "HyperTwist|Training|Coach")
FHyperTwistTrainingCoachFocusProvenanceSurface
GetDisplayedCoachFocusProvenanceSurface() const;
UFUNCTION(BlueprintCallable, Category = "HyperTwist|Training|Coach")
FHyperTwistTrainingRunState StartCoachRecommendedRun(
int32 MaxCases,

View file

@ -209,6 +209,10 @@ public:
FHyperTwistTrainingCoachHandoffContinuationSurface
GetDisplayedCoachHandoffContinuationSurface() const;
UFUNCTION(BlueprintPure, Category = "HyperTwist|Training|Coach")
FHyperTwistTrainingCoachFocusProvenanceSurface
GetDisplayedCoachFocusProvenanceSurface() const;
UFUNCTION(BlueprintCallable, Category = "HyperTwist|Training|Coach")
FHyperTwistTrainingRunState StartCoachRecommendedTrainingRun(int32 MaxCases);

View file

@ -8016,3 +8016,80 @@ struct FHyperTwistTrainingCoachHandoffContinuationSurface
|| !QueueRecoveryStatusLine.IsEmpty();
}
};
USTRUCT(BlueprintType)
struct FHyperTwistTrainingCoachFocusProvenanceSurface
{
GENERATED_BODY()
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString Headline;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString FocusHeadline;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString FocusSummaryLine;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString ProvenanceSummaryLine;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString DetailLine;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString FocusDeckId;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString FocusMethodSegmentId;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString ActiveActionLabel;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString ActiveSourceLabel;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString ActiveSourceDescription;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString GuidanceSourceDescription;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString HandoffSourceDescription;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString QueueSourceDescription;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString RecoverySourceDescription;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString LaunchBudgetSourceDescription;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
EHyperTwistTrainingCoachHandoffKind ContinuationKind =
EHyperTwistTrainingCoachHandoffKind::None;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
EHyperTwistTrainingCoachQueuePostureKind QueuePostureKind =
EHyperTwistTrainingCoachQueuePostureKind::None;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
EHyperTwistTrainingCoachGuidanceLane PreferredGuidanceLane =
EHyperTwistTrainingCoachGuidanceLane::None;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
bool bHasFocusTarget = false;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
bool bHasUpstreamProvenance = false;
bool IsStructurallyValid() const
{
return !FocusSummaryLine.IsEmpty()
|| !ProvenanceSummaryLine.IsEmpty()
|| bHasFocusTarget
|| bHasUpstreamProvenance;
}
};

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 coach handoff recommendation / continuation status surface over the already-landed primary CTA, secondary queue-control, and queue-recovery posture surfaces, so gameplay/Blueprint callers can render the queued-vs-follow-up-vs-recommended continuation posture without reaching into raw panel-state strings or handoff enums directly
- the current next bounded packet is to expose one thin coach focus/provenance summary surface on those same two consumer surfaces so gameplay/Blueprint callers can render the active coach target and upstream recommendation provenance without reading raw focus ids or source-label strings directly
- the latest landed bounded `Phase 4` packet stays on that same gameplay-facing consumer lane and adds one thin coach focus/provenance summary surface over the already-landed primary CTA, secondary queue-control, queue-recovery posture, and handoff-continuation surfaces, so gameplay/Blueprint callers can render the active coach target and upstream recommendation provenance without reading raw focus ids or source-label strings directly
- the current next bounded packet is to expose one thin coach workload/budget summary surface on those same two consumer surfaces so gameplay/Blueprint callers can render launch case budget, queue load, and focus pressure without reading raw count and budget fields directly
- the current execution-discipline rule that broad refactor / monolith-splitting work should not interrupt the active bounded roadmap packet unless structure is actually blocking it
## Current execution-reality references

View file

@ -0,0 +1,114 @@
# HyperTwist Phase 4 gameplay focus provenance surface packet
Created on `2026-05-07`
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 coach helpers by exposing one pure focus/provenance summary surface over the already-landed primary coach CTA, queue-control, queue-recovery posture, and handoff-continuation surfaces.
The open tasks were:
- stop forcing gameplay / Blueprint callers to read raw focus deck ids, focus method-segment ids, and source-label strings just to describe the active coach target and why it is the active target
- expose one thin first-party focus/provenance surface that summarizes the current coach target, the active action provenance, and the upstream guidance / handoff / recovery / launch-budget sources
- keep that surface derived from already-cached gameplay consumer state instead of widening into dashboard-only provenance composition or retained recommendation history
It is not:
- a new recommendation policy packet
- a dashboard provenance migration
- a broader gameplay HUD composition pass
- a backend continuity or template-analytics packet
## Scope
Bounded lane:
- add a first-party focus/provenance surface in shared training types
- add a coach-library helper that derives the surface from panel state, the displayed primary CTA surface, the displayed queue-control surface, the displayed queue-recovery posture surface, and the displayed handoff-continuation surface
- expose that derived focus/provenance surface on `HyperTwistTrainingPanelWidget` and `HyperTwistTrainingSessionActor`
- keep the derivation presentation-only and rooted in already-cached gameplay consumer state
Out of scope:
- importing the dashboards deeper retained provenance views into gameplay consumers
- changing coach handoff branch order
- changing queue recovery policy
- widening into broader gameplay orchestration in the same packet
## Why this was the right next packet
Before this slice:
- gameplay-facing panel/session consumers already exposed a truthful primary CTA surface
- those surfaces already exposed truthful queue-control, queue-recovery posture, and handoff-continuation surfaces
But one thin consumer seam was still open:
- callers could bind actions and narrate continuation posture
- but they still had to inspect raw focus ids and source-label strings to explain what the current coach target actually was and what upstream path produced it
That meant:
- action binding was thin
- continuation posture was thin
- but target/provenance explanation still leaked raw state into callers
So the next bounded move was:
- keep the already-landed action and posture surfaces unchanged
- and expose one narrow focus/provenance summary surface above them
## What landed
Primary code changes:
- `UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingTypes.h`
- added `FHyperTwistTrainingCoachFocusProvenanceSurface`
- `UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingCoachLibrary.h`
- `UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingCoachLibrary.cpp`
- added `DeriveCoachFocusProvenanceSurface(...)`
- focus/provenance is derived from existing panel-state, primary CTA, queue-control, queue-recovery posture, and handoff-continuation inputs
- `UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingPanelWidget.h`
- `UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingPanelWidget.cpp`
- added `GetDisplayedCoachFocusProvenanceSurface()`
- `UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingSessionActor.h`
- `UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingSessionActor.cpp`
- added `GetDisplayedCoachFocusProvenanceSurface()`
## Product effect
The gameplay-facing coach consumer layer is now more complete:
- gameplay / Blueprint callers can read one pure surface to describe the active coach target and its upstream provenance
- callers no longer need to inspect raw focus ids and source-label strings just to render target/provenance narration
- the surface stays aligned with the already-landed primary CTA, queue-control, queue-recovery posture, and handoff-continuation surfaces without importing dashboard-only retained provenance composition
## Acceptance criteria
- shared training types expose a narrow focus/provenance surface
- coach-library derivation consumes the already-cached gameplay consumer state rather than adding new runtime fetches
- panel-widget callers can fetch the displayed focus/provenance surface directly
- session-actor callers can fetch the displayed focus/provenance surface directly
- full product build succeeds
## Validation checklist
1. build `UnrealHyperTwist.sln` / `UnrealHyperTwistEditor`
2. confirm the new focus/provenance surface compiles in shared types and coach library
3. confirm panel-widget getters compile and return the derived focus/provenance surface
4. confirm session-actor getters compile and return the derived focus/provenance surface
5. confirm no backend continuity or dashboard-only provenance composition was pulled into the packet
## Validation result
- full `Development Editor|Win64` build succeeded on `2026-05-07`
- build completed with `0` warnings and `0` errors
## Next bounded follow-on slice
- stay on the same gameplay-facing consumer lane and expose one thin coach workload/budget summary surface on `HyperTwistTrainingPanelWidget` and `HyperTwistTrainingSessionActor`, so gameplay callers can render launch case budget, queue load, and focus pressure without reading raw count and budget fields directly