diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingCoachLibrary.cpp b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingCoachLibrary.cpp index 1806c2d..c00f7b8 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingCoachLibrary.cpp +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingCoachLibrary.cpp @@ -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; +} diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingPanelWidget.cpp b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingPanelWidget.cpp index 53c7780..a6c34ae 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingPanelWidget.cpp +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingPanelWidget.cpp @@ -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 diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingSessionActor.cpp b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingSessionActor.cpp index 57e72ea..61e1869 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingSessionActor.cpp +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingSessionActor.cpp @@ -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 ) diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingCoachLibrary.h b/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingCoachLibrary.h index e72763c..fc3e7a6 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingCoachLibrary.h +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingCoachLibrary.h @@ -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 + ); }; diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingPanelWidget.h b/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingPanelWidget.h index 3326a37..3005f9a 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingPanelWidget.h +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingPanelWidget.h @@ -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, diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingSessionActor.h b/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingSessionActor.h index e277ed6..c13ba33 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingSessionActor.h +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingSessionActor.h @@ -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); diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingTypes.h b/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingTypes.h index 3ea3e38..439e152 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingTypes.h +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingTypes.h @@ -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; + } +}; diff --git a/docs/HYPERTWIST_ROADMAP_OVERHAUL_EXPANSION_GUIDE.md b/docs/HYPERTWIST_ROADMAP_OVERHAUL_EXPANSION_GUIDE.md index 4366e82..4633b51 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 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 diff --git a/docs/arch/HYPERTWIST_PHASE4_GAMEPLAY_FOCUS_PROVENANCE_SURFACE_PACKET_2026-05-07.md b/docs/arch/HYPERTWIST_PHASE4_GAMEPLAY_FOCUS_PROVENANCE_SURFACE_PACKET_2026-05-07.md new file mode 100644 index 0000000..bfea590 --- /dev/null +++ b/docs/arch/HYPERTWIST_PHASE4_GAMEPLAY_FOCUS_PROVENANCE_SURFACE_PACKET_2026-05-07.md @@ -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 dashboard’s 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