From db016a07895aa23495ad944b198ea503e40f8a23 Mon Sep 17 00:00:00 2001 From: axiomlogicnexus Date: Sat, 9 May 2026 18:49:42 +0200 Subject: [PATCH] Expose dashboard live guidance inspect surfaces --- .../HyperTwistCoachDashboardWidget.cpp | 597 ++++++++++++++++++ .../HyperTwistCoachDashboardWidget.h | 12 + .../HyperTwistTrainingTypes.h | 362 +++++++++++ ...RTWIST_ROADMAP_OVERHAUL_EXPANSION_GUIDE.md | 4 +- ...HTING_INSPECT_SURFACE_PACKET_2026-05-09.md | 54 ++ 5 files changed, 1027 insertions(+), 2 deletions(-) create mode 100644 docs/arch/HYPERTWIST_PHASE4_DASHBOARD_LIVE_GUIDANCE_COMPARISON_OUTCOME_DECISION_WEIGHTING_INSPECT_SURFACE_PACKET_2026-05-09.md diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistCoachDashboardWidget.cpp b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistCoachDashboardWidget.cpp index c4bf67f..c5a3986 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistCoachDashboardWidget.cpp +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistCoachDashboardWidget.cpp @@ -8509,6 +8509,603 @@ UHyperTwistCoachDashboardWidget::GetSelectedTemplateLaunchHistoryInspectSurface( return Surface; } +FHyperTwistTrainingCoachDashboardGuidanceComparisonInspectSurface +UHyperTwistCoachDashboardWidget::GetDisplayedGuidanceComparisonInspectSurface() const +{ + FHyperTwistTrainingCoachDashboardGuidanceComparisonInspectSurface Surface; + Surface.Headline = TEXT("Guidance comparison"); + Surface.bClosedLoopIdleActionability = IsCoachLoopActionabilityClosed(); + + const auto BuildLaneLabel = + [](const EHyperTwistCoachDashboardGuidancePreviewLane Lane) -> FString + { + return Lane == EHyperTwistCoachDashboardGuidancePreviewLane::Recommended + ? TEXT("recommended") + : (Lane == EHyperTwistCoachDashboardGuidancePreviewLane::FollowUp + ? TEXT("follow-up") + : TEXT("none")); + }; + const auto BuildDeckLabel = + [](const FHyperTwistTrainingDeck* Deck) -> FString + { + if (Deck == nullptr || !Deck->IsStructurallyValid()) + { + return TEXT("n/a"); + } + + return Deck->Title.IsEmpty() ? Deck->DeckId : Deck->Title; + }; + + const bool bHasCurrentRunRecap = CachedRunState.Session.IsStructurallyValid() + && (CachedRunState.Session.SessionState == EHyperTwistTrainingSessionState::Completed + || CachedRunState.Session.SessionState == EHyperTwistTrainingSessionState::Aborted); + const FHyperTwistTrainingSessionSummary* DisplayedRunRecap = bHasCurrentRunRecap + ? &CachedRunSummary + : (bHasRetainedRunRecap ? &RetainedRunRecapSummary : nullptr); + const FHyperTwistTrainingDeck* DisplayedRunRecapDeck = bHasCurrentRunRecap + ? &CachedRunState.ActiveDeck + : (bHasRetainedRunRecap ? &RetainedRunRecapDeck : nullptr); + const EHyperTwistTrainingDeliveryMode DisplayedRunRecapMode = bHasCurrentRunRecap + ? CachedRunState.Session.Mode + : RetainedRunRecapMode; + const bool bHasDisplayedRunRecap = DisplayedRunRecap != nullptr + && !DisplayedRunRecap->TrainingSessionId.IsEmpty(); + Surface.bHasDisplayedRunRecap = bHasDisplayedRunRecap; + Surface.RunRecapSourceLabel = bHasDisplayedRunRecap + ? (bHasCurrentRunRecap ? TEXT("current run") : TEXT("last completed run")) + : TEXT("none"); + Surface.TrainingSessionId = bHasDisplayedRunRecap + ? DisplayedRunRecap->TrainingSessionId + : TEXT("n/a"); + Surface.ActualDeckLabel = BuildDeckLabel(DisplayedRunRecapDeck); + Surface.ActualModeLabel = + bHasDisplayedRunRecap + ? HyperTwistCoachDashboardWidgetInternal::EnumDisplayName(DisplayedRunRecapMode) + : TEXT("n/a"); + Surface.ActualSelectionLabel = + DisplayedRunRecapDeck != nullptr && DisplayedRunRecapDeck->IsStructurallyValid() + ? HyperTwistCoachDashboardWidgetInternal::EnumDisplayName( + DisplayedRunRecapDeck->SelectionPolicy) + : TEXT("n/a"); + Surface.LastCaseId = + bHasDisplayedRunRecap && !DisplayedRunRecap->LastCaseId.IsEmpty() + ? DisplayedRunRecap->LastCaseId + : TEXT("n/a"); + Surface.CompletedAttemptCount = + bHasDisplayedRunRecap ? DisplayedRunRecap->CompletedAttemptCount : 0; + Surface.FailureCount = bHasDisplayedRunRecap ? DisplayedRunRecap->FailureCount : 0; + Surface.TimeoutCount = bHasDisplayedRunRecap ? DisplayedRunRecap->TimeoutCount : 0; + Surface.DNFCount = bHasDisplayedRunRecap ? DisplayedRunRecap->DNFCount : 0; + Surface.TotalMistakes = bHasDisplayedRunRecap ? DisplayedRunRecap->TotalMistakes : 0; + Surface.SuccessRate = bHasDisplayedRunRecap ? DisplayedRunRecap->SuccessRate : 0.0f; + + const FHyperTwistTrainingSessionSummary* DisplayedGuidanceComparisonRunRecap = nullptr; + const FHyperTwistCoachBrief* DisplayedGuidanceComparisonBrief = nullptr; + const FHyperTwistTrainingDeck* DisplayedGuidanceComparisonDeck = nullptr; + FString DisplayedGuidanceComparisonSelectedCaseId; + bool bDisplayedGuidanceComparisonSingleCase = false; + Surface.bHasComparisonContext = ResolveDisplayedGuidanceComparisonContext( + DisplayedGuidanceComparisonRunRecap, + DisplayedGuidanceComparisonBrief, + DisplayedGuidanceComparisonDeck, + DisplayedGuidanceComparisonSelectedCaseId, + bDisplayedGuidanceComparisonSingleCase); + Surface.bHasDisplayedGuidanceBrief = DisplayedGuidanceComparisonBrief != nullptr; + Surface.bHasDisplayedGuidanceDeck = + DisplayedGuidanceComparisonDeck != nullptr + && DisplayedGuidanceComparisonDeck->IsStructurallyValid(); + Surface.bSingleCaseScope = bDisplayedGuidanceComparisonSingleCase; + Surface.ScopeLabel = Surface.bHasComparisonContext + ? (Surface.bSingleCaseScope ? TEXT("selected case") : TEXT("full preview deck")) + : TEXT("none"); + + EHyperTwistCoachDashboardGuidancePreviewLane DisplayedGuidanceComparisonLane = + EHyperTwistCoachDashboardGuidancePreviewLane::None; + if (DisplayedGuidanceComparisonBrief == &ActiveGuidanceLaunchBrief) + { + DisplayedGuidanceComparisonLane = ActiveGuidanceLaunchLane; + } + else if (DisplayedGuidanceComparisonBrief == &RetainedGuidanceComparisonBrief) + { + DisplayedGuidanceComparisonLane = RetainedGuidanceComparisonLane; + } + + Surface.GuidanceLaneLabel = BuildLaneLabel(DisplayedGuidanceComparisonLane); + Surface.BriefHeadline = + DisplayedGuidanceComparisonBrief != nullptr + && !DisplayedGuidanceComparisonBrief->Headline.IsEmpty() + ? DisplayedGuidanceComparisonBrief->Headline + : TEXT("n/a"); + Surface.PrimaryActionLabel = + DisplayedGuidanceComparisonBrief != nullptr + && !DisplayedGuidanceComparisonBrief->PrimaryActionLabel.IsEmpty() + ? DisplayedGuidanceComparisonBrief->PrimaryActionLabel + : TEXT("n/a"); + Surface.PreviewDeckLabel = BuildDeckLabel(DisplayedGuidanceComparisonDeck); + Surface.ExpectedModeLabel = + DisplayedGuidanceComparisonBrief != nullptr + ? HyperTwistCoachDashboardWidgetInternal::EnumDisplayName( + DisplayedGuidanceComparisonBrief->SuggestedMode) + : TEXT("n/a"); + Surface.ExpectedSelectionLabel = + DisplayedGuidanceComparisonBrief != nullptr + ? HyperTwistCoachDashboardWidgetInternal::EnumDisplayName( + DisplayedGuidanceComparisonBrief->SuggestedSelectionPolicy) + : TEXT("n/a"); + Surface.FocusLaneLabel = + DisplayedGuidanceComparisonBrief != nullptr + ? HyperTwistCoachDashboardWidgetInternal::EnumDisplayName( + DisplayedGuidanceComparisonBrief->FocusLane) + : TEXT("n/a"); + Surface.SelectedCaseId = + !DisplayedGuidanceComparisonSelectedCaseId.IsEmpty() + ? DisplayedGuidanceComparisonSelectedCaseId + : TEXT("n/a"); + Surface.ExpectedCaseCount = + DisplayedGuidanceComparisonDeck != nullptr ? DisplayedGuidanceComparisonDeck->Cases.Num() : 0; + Surface.FocusCaseCount = + DisplayedGuidanceComparisonBrief != nullptr + ? DisplayedGuidanceComparisonBrief->FocusCaseIds.Num() + : 0; + Surface.bSelectedCaseMatchesLastCase = + bHasDisplayedRunRecap + && !DisplayedGuidanceComparisonSelectedCaseId.IsEmpty() + && DisplayedRunRecap->LastCaseId == DisplayedGuidanceComparisonSelectedCaseId; + + Surface.StatusLine = Surface.bHasComparisonContext + ? FString::Printf( + TEXT("Comparison: %s recap vs %s preview | Scope: %s | Focus lane: %s | Expected cases: %d | Completed attempts: %d | Success rate: %.2f"), + *Surface.RunRecapSourceLabel, + *Surface.GuidanceLaneLabel, + Surface.bSingleCaseScope ? TEXT("selected case") : TEXT("full preview deck"), + *Surface.FocusLaneLabel, + Surface.ExpectedCaseCount, + Surface.CompletedAttemptCount, + Surface.SuccessRate) + : TEXT("Comparison: complete a run launched from previewed guidance to compare the recap against the original coach intent."); + Surface.DetailLine = Surface.bHasComparisonContext + ? FString::Printf( + TEXT("Launch brief: %s | Action: %s | Preview deck: %s | Actual deck: %s | Mode expected/actual: %s / %s | Selection expected/actual: %s / %s | Selected case: %s | Last case matched: %s | Focus cases: %d | Last case: %s"), + *Surface.BriefHeadline, + *Surface.PrimaryActionLabel, + *Surface.PreviewDeckLabel, + *Surface.ActualDeckLabel, + *Surface.ExpectedModeLabel, + *Surface.ActualModeLabel, + *Surface.ExpectedSelectionLabel, + *Surface.ActualSelectionLabel, + *Surface.SelectedCaseId, + Surface.bSelectedCaseMatchesLastCase ? TEXT("yes") : TEXT("no"), + Surface.FocusCaseCount, + *Surface.LastCaseId) + : TEXT("Comparison detail: no preview-launched recap is currently selected."); + if (Surface.bClosedLoopIdleActionability + && !Surface.bHasComparisonContext) + { + Surface.StatusLine = + TEXT("Comparison: retained comparison history remains inspectable, but no preview-launched recap is currently selected in the closed-loop idle state."); + Surface.DetailLine = + TEXT("Comparison detail: the coach-to-review loop is closed and idle, so no live recommended or follow-up comparison target remains active."); + } + return Surface; +} + +FHyperTwistTrainingCoachDashboardGuidanceOutcomeInspectSurface +UHyperTwistCoachDashboardWidget::GetDisplayedGuidanceOutcomeInspectSurface() const +{ + FHyperTwistTrainingCoachDashboardGuidanceOutcomeInspectSurface Surface; + Surface.Headline = TEXT("Guidance outcome"); + Surface.bClosedLoopIdleActionability = IsCoachLoopActionabilityClosed(); + + const auto BuildLaneLabel = + [](const EHyperTwistCoachDashboardGuidancePreviewLane Lane) -> FString + { + return Lane == EHyperTwistCoachDashboardGuidancePreviewLane::Recommended + ? TEXT("recommended") + : (Lane == EHyperTwistCoachDashboardGuidancePreviewLane::FollowUp + ? TEXT("follow-up") + : TEXT("none")); + }; + const auto BuildDecisionActionLabel = + [this](const EHyperTwistCoachDashboardOutcomeAction Action) -> FString + { + return Action == EHyperTwistCoachDashboardOutcomeAction::None + ? TEXT("none") + : BuildGuidanceOutcomeActionLabel(Action); + }; + + const bool bHasCurrentRunRecap = CachedRunState.Session.IsStructurallyValid() + && (CachedRunState.Session.SessionState == EHyperTwistTrainingSessionState::Completed + || CachedRunState.Session.SessionState == EHyperTwistTrainingSessionState::Aborted); + const FHyperTwistTrainingSessionSummary* DisplayedRunRecap = bHasCurrentRunRecap + ? &CachedRunSummary + : (bHasRetainedRunRecap ? &RetainedRunRecapSummary : nullptr); + Surface.bHasDisplayedRunRecap = + DisplayedRunRecap != nullptr && !DisplayedRunRecap->TrainingSessionId.IsEmpty(); + Surface.RunRecapSourceLabel = Surface.bHasDisplayedRunRecap + ? (bHasCurrentRunRecap ? TEXT("current run") : TEXT("last completed run")) + : TEXT("none"); + Surface.TrainingSessionId = Surface.bHasDisplayedRunRecap + ? DisplayedRunRecap->TrainingSessionId + : TEXT("n/a"); + + const FHyperTwistTrainingSessionSummary* DisplayedGuidanceComparisonRunRecap = nullptr; + const FHyperTwistCoachBrief* DisplayedGuidanceComparisonBrief = nullptr; + const FHyperTwistTrainingDeck* DisplayedGuidanceComparisonDeck = nullptr; + FString DisplayedGuidanceComparisonSelectedCaseId; + bool bDisplayedGuidanceComparisonSingleCase = false; + Surface.bHasComparisonContext = ResolveDisplayedGuidanceComparisonContext( + DisplayedGuidanceComparisonRunRecap, + DisplayedGuidanceComparisonBrief, + DisplayedGuidanceComparisonDeck, + DisplayedGuidanceComparisonSelectedCaseId, + bDisplayedGuidanceComparisonSingleCase); + Surface.bHasDisplayedGuidanceBrief = DisplayedGuidanceComparisonBrief != nullptr; + Surface.bHasDisplayedGuidanceDeck = + DisplayedGuidanceComparisonDeck != nullptr + && DisplayedGuidanceComparisonDeck->IsStructurallyValid(); + Surface.bSingleCaseScope = bDisplayedGuidanceComparisonSingleCase; + + EHyperTwistCoachDashboardGuidancePreviewLane DisplayedGuidanceComparisonLane = + EHyperTwistCoachDashboardGuidancePreviewLane::None; + if (DisplayedGuidanceComparisonBrief == &ActiveGuidanceLaunchBrief) + { + DisplayedGuidanceComparisonLane = ActiveGuidanceLaunchLane; + } + else if (DisplayedGuidanceComparisonBrief == &RetainedGuidanceComparisonBrief) + { + DisplayedGuidanceComparisonLane = RetainedGuidanceComparisonLane; + } + + Surface.GuidanceLaneLabel = BuildLaneLabel(DisplayedGuidanceComparisonLane); + Surface.ScopeLabel = Surface.bHasComparisonContext + ? (Surface.bSingleCaseScope ? TEXT("selected case") : TEXT("preview deck")) + : TEXT("none"); + Surface.BriefHeadline = + DisplayedGuidanceComparisonBrief != nullptr + && !DisplayedGuidanceComparisonBrief->Headline.IsEmpty() + ? DisplayedGuidanceComparisonBrief->Headline + : TEXT("n/a"); + Surface.SelectedCaseId = + !DisplayedGuidanceComparisonSelectedCaseId.IsEmpty() + ? DisplayedGuidanceComparisonSelectedCaseId + : TEXT("n/a"); + Surface.ExpectedCaseCount = + DisplayedGuidanceComparisonDeck != nullptr ? DisplayedGuidanceComparisonDeck->Cases.Num() : 0; + Surface.CompletedAttemptCount = + DisplayedRunRecap != nullptr ? DisplayedRunRecap->CompletedAttemptCount : 0; + Surface.NonSuccessCount = + DisplayedRunRecap != nullptr + ? (DisplayedRunRecap->FailureCount + + DisplayedRunRecap->TimeoutCount + + DisplayedRunRecap->DNFCount) + : 0; + Surface.TotalMistakes = DisplayedRunRecap != nullptr ? DisplayedRunRecap->TotalMistakes : 0; + Surface.SuccessRate = DisplayedRunRecap != nullptr ? DisplayedRunRecap->SuccessRate : 0.0f; + + Surface.StatusLine = + TEXT("Outcome: complete a run launched from previewed guidance to derive a repeat, broaden, or redirect verdict."); + Surface.DetailLine = + TEXT("Outcome detail: no preview-launched recap is currently selected."); + if (DisplayedRunRecap != nullptr + && DisplayedGuidanceComparisonBrief != nullptr + && DisplayedGuidanceComparisonDeck != nullptr + && DisplayedRunRecap->CompletedAttemptCount > 0) + { + if (Surface.bSingleCaseScope) + { + if (DisplayedRunRecap->SuccessRate >= 0.85f + && DisplayedRunRecap->TimeoutCount == 0 + && DisplayedRunRecap->DNFCount == 0 + && DisplayedRunRecap->TotalMistakes <= 1) + { + Surface.StatusLine = + TEXT("Outcome: broaden guidance. The selected case looks stable enough to expand back to the wider preview deck."); + Surface.DetailLine = FString::Printf( + TEXT("Recommendation: move from the single-case launch into the broader %s preview deck or the next coach-directed handoff. Success rate %.2f across %d completed attempts with %d mistakes."), + *Surface.GuidanceLaneLabel, + DisplayedRunRecap->SuccessRate, + DisplayedRunRecap->CompletedAttemptCount, + DisplayedRunRecap->TotalMistakes); + } + else if (DisplayedRunRecap->SuccessRate >= 0.5f) + { + Surface.StatusLine = + TEXT("Outcome: stabilize, then broaden. The selected case is moving, but it is not yet clean enough for a wider shift."); + Surface.DetailLine = FString::Printf( + TEXT("Recommendation: repeat the selected preview case or a very small scope before broadening. Success rate %.2f with %d non-success outcomes and %d mistakes."), + DisplayedRunRecap->SuccessRate, + Surface.NonSuccessCount, + DisplayedRunRecap->TotalMistakes); + } + else + { + Surface.StatusLine = + TEXT("Outcome: repeat selected focus. The previewed case is still weak enough that broadening would be premature."); + Surface.DetailLine = FString::Printf( + TEXT("Recommendation: stay on the selected preview case until the recap is cleaner. Success rate %.2f with %d non-success outcomes and %d mistakes."), + DisplayedRunRecap->SuccessRate, + Surface.NonSuccessCount, + DisplayedRunRecap->TotalMistakes); + } + } + else if (DisplayedRunRecap->SuccessRate >= 0.8f + && Surface.NonSuccessCount <= FMath::Max(1, DisplayedRunRecap->CompletedAttemptCount / 4) + && DisplayedRunRecap->TotalMistakes <= FMath::Max(1, DisplayedRunRecap->CompletedAttemptCount / 2)) + { + Surface.StatusLine = + TEXT("Outcome: change guidance. The full preview deck looks stable enough to move on from this exact focus."); + Surface.DetailLine = FString::Printf( + TEXT("Recommendation: use follow-up, queued, or recommended coach handoff rather than repeating the same deck. Success rate %.2f across %d completed attempts with %d mistakes."), + DisplayedRunRecap->SuccessRate, + DisplayedRunRecap->CompletedAttemptCount, + DisplayedRunRecap->TotalMistakes); + } + else if (DisplayedRunRecap->SuccessRate >= 0.55f) + { + Surface.StatusLine = + TEXT("Outcome: repeat and stabilize. The deck is moving, but not cleanly enough to abandon this scope."); + Surface.DetailLine = FString::Printf( + TEXT("Recommendation: repeat the preview deck or stay near this focus before changing guidance. Success rate %.2f with %d non-success outcomes and %d mistakes."), + DisplayedRunRecap->SuccessRate, + Surface.NonSuccessCount, + DisplayedRunRecap->TotalMistakes); + } + else + { + Surface.StatusLine = + TEXT("Outcome: narrow or redirect guidance. The broad preview scope is still too unstable."); + Surface.DetailLine = FString::Printf( + TEXT("Recommendation: narrow back to a smaller focus case set or switch guidance lanes before repeating the full deck. Success rate %.2f with %d non-success outcomes and %d mistakes."), + DisplayedRunRecap->SuccessRate, + Surface.NonSuccessCount, + DisplayedRunRecap->TotalMistakes); + } + } + + const EHyperTwistCoachDashboardOutcomeAction BaseGuidanceOutcomeAction = + DeriveGuidanceOutcomeAction(); + const FHyperTwistCoachDashboardDecisionWeighting GuidanceDecisionWeighting = + BuildGuidanceDecisionWeighting(BaseGuidanceOutcomeAction); + const EHyperTwistCoachDashboardOutcomeAction GuidanceOutcomeAction = + GuidanceDecisionWeighting.WeightedAction != EHyperTwistCoachDashboardOutcomeAction::None + ? GuidanceDecisionWeighting.WeightedAction + : BaseGuidanceOutcomeAction; + Surface.BaseActionLabel = BuildDecisionActionLabel(BaseGuidanceOutcomeAction); + Surface.WeightedActionLabel = + BuildDecisionActionLabel(GuidanceDecisionWeighting.WeightedAction); + Surface.bHasWeightingOverride = GuidanceDecisionWeighting.bHasOverride; + Surface.bUsedClosureHistorySignal = + GuidanceDecisionWeighting.bUsedClosureHistorySignal; + Surface.bUsedDeferredOverflowSignal = + GuidanceDecisionWeighting.bUsedDeferredOverflowSignal; + if (GuidanceDecisionWeighting.bUsedClosureHistorySignal) + { + Surface.StatusLine += TEXT(" Closure-recovery history adjusted this recommendation."); + } + if (GuidanceDecisionWeighting.bUsedDeferredOverflowSignal) + { + Surface.StatusLine += + TEXT(" Deferred-overflow continuation history adjusted this recommendation."); + } + + Surface.bCanRedirectAction = + CachedCoachPanelState.bCanStartCoachFollowUpRun + || CachedCoachPanelState.bCanStartQueuedRun + || CachedCoachPanelState.bCanStartCoachRecommendedRun; + const bool bHasActiveRun = CachedRunState.Session.IsStructurallyValid() + && CachedRunState.ActiveDeck.IsStructurallyValid(); + Surface.bCanApplyAction = + GuidanceOutcomeAction != EHyperTwistCoachDashboardOutcomeAction::None + && !bHasLiveAttemptTimer + && (!bHasActiveRun + || CachedRunState.Session.SessionState == EHyperTwistTrainingSessionState::Completed + || CachedRunState.Session.SessionState == EHyperTwistTrainingSessionState::Aborted) + && (GuidanceOutcomeAction != EHyperTwistCoachDashboardOutcomeAction::RedirectCoachGuidance + || Surface.bCanRedirectAction); + Surface.ActionLabel = BuildGuidanceOutcomeActionLabel(GuidanceOutcomeAction); + if (Surface.bClosedLoopIdleActionability) + { + Surface.StatusLine = + TEXT("Outcome: retained comparison history remains inspectable, but the coach-to-review loop is closed and idle."); + Surface.DetailLine = + TEXT("Outcome detail: no live repeat, broaden, or redirect action remains while the closed-loop idle boundary holds."); + Surface.ActionLabel = TEXT("Loop Idle"); + Surface.bCanApplyAction = false; + } + return Surface; +} + +FHyperTwistTrainingCoachDashboardGuidanceDecisionWeightingInspectSurface +UHyperTwistCoachDashboardWidget::GetDisplayedGuidanceDecisionWeightingInspectSurface() const +{ + FHyperTwistTrainingCoachDashboardGuidanceDecisionWeightingInspectSurface Surface; + Surface.Headline = TEXT("Decision weighting"); + Surface.bClosedLoopIdleActionability = IsCoachLoopActionabilityClosed(); + + const auto BuildLaneLabel = + [](const EHyperTwistCoachDashboardGuidancePreviewLane Lane) -> FString + { + return Lane == EHyperTwistCoachDashboardGuidancePreviewLane::Recommended + ? TEXT("recommended") + : (Lane == EHyperTwistCoachDashboardGuidancePreviewLane::FollowUp + ? TEXT("follow-up") + : TEXT("none")); + }; + const auto BuildDecisionActionLabel = + [this](const EHyperTwistCoachDashboardOutcomeAction Action) -> FString + { + return Action == EHyperTwistCoachDashboardOutcomeAction::None + ? TEXT("none") + : BuildGuidanceOutcomeActionLabel(Action); + }; + + const bool bHasCurrentRunRecap = CachedRunState.Session.IsStructurallyValid() + && (CachedRunState.Session.SessionState == EHyperTwistTrainingSessionState::Completed + || CachedRunState.Session.SessionState == EHyperTwistTrainingSessionState::Aborted); + const FHyperTwistTrainingSessionSummary* DisplayedRunRecap = bHasCurrentRunRecap + ? &CachedRunSummary + : (bHasRetainedRunRecap ? &RetainedRunRecapSummary : nullptr); + Surface.bHasDisplayedRunRecap = + DisplayedRunRecap != nullptr && !DisplayedRunRecap->TrainingSessionId.IsEmpty(); + Surface.RunRecapSourceLabel = Surface.bHasDisplayedRunRecap + ? (bHasCurrentRunRecap ? TEXT("current run") : TEXT("last completed run")) + : TEXT("none"); + Surface.TrainingSessionId = Surface.bHasDisplayedRunRecap + ? DisplayedRunRecap->TrainingSessionId + : TEXT("n/a"); + + const FHyperTwistTrainingSessionSummary* DisplayedGuidanceComparisonRunRecap = nullptr; + const FHyperTwistCoachBrief* DisplayedGuidanceComparisonBrief = nullptr; + const FHyperTwistTrainingDeck* DisplayedGuidanceComparisonDeck = nullptr; + FString DisplayedGuidanceComparisonSelectedCaseId; + bool bDisplayedGuidanceComparisonSingleCase = false; + Surface.bHasComparisonContext = ResolveDisplayedGuidanceComparisonContext( + DisplayedGuidanceComparisonRunRecap, + DisplayedGuidanceComparisonBrief, + DisplayedGuidanceComparisonDeck, + DisplayedGuidanceComparisonSelectedCaseId, + bDisplayedGuidanceComparisonSingleCase); + Surface.bHasDisplayedGuidanceBrief = DisplayedGuidanceComparisonBrief != nullptr; + Surface.bHasDisplayedGuidanceDeck = + DisplayedGuidanceComparisonDeck != nullptr + && DisplayedGuidanceComparisonDeck->IsStructurallyValid(); + Surface.bSingleCaseScope = bDisplayedGuidanceComparisonSingleCase; + + EHyperTwistCoachDashboardGuidancePreviewLane DisplayedGuidanceComparisonLane = + EHyperTwistCoachDashboardGuidancePreviewLane::None; + if (DisplayedGuidanceComparisonBrief == &ActiveGuidanceLaunchBrief) + { + DisplayedGuidanceComparisonLane = ActiveGuidanceLaunchLane; + } + else if (DisplayedGuidanceComparisonBrief == &RetainedGuidanceComparisonBrief) + { + DisplayedGuidanceComparisonLane = RetainedGuidanceComparisonLane; + } + + Surface.GuidanceLaneLabel = BuildLaneLabel(DisplayedGuidanceComparisonLane); + Surface.ScopeLabel = Surface.bHasComparisonContext + ? (Surface.bSingleCaseScope ? TEXT("selected case") : TEXT("preview deck")) + : TEXT("none"); + Surface.BriefHeadline = + DisplayedGuidanceComparisonBrief != nullptr + && !DisplayedGuidanceComparisonBrief->Headline.IsEmpty() + ? DisplayedGuidanceComparisonBrief->Headline + : TEXT("n/a"); + Surface.SelectedCaseId = + !DisplayedGuidanceComparisonSelectedCaseId.IsEmpty() + ? DisplayedGuidanceComparisonSelectedCaseId + : TEXT("n/a"); + + const EHyperTwistCoachDashboardOutcomeAction BaseAction = DeriveGuidanceOutcomeAction(); + const FHyperTwistCoachDashboardDecisionWeighting Weighting = + BuildGuidanceDecisionWeighting(BaseAction); + const EHyperTwistCoachDashboardOutcomeAction ResolvedAction = + Weighting.WeightedAction != EHyperTwistCoachDashboardOutcomeAction::None + ? Weighting.WeightedAction + : BaseAction; + Surface.ResolvedActionLabel = BuildGuidanceOutcomeActionLabel(ResolvedAction); + Surface.BaseActionLabel = BuildDecisionActionLabel(Weighting.BaseAction); + Surface.WeightedActionLabel = BuildDecisionActionLabel(Weighting.WeightedAction); + Surface.LastAppliedActionLabel = BuildDecisionActionLabel(Weighting.LastAppliedAction); + Surface.MatchingDecisionCount = Weighting.MatchingDecisionCount; + Surface.RepeatSelectedCaseCount = Weighting.RepeatSelectedCaseCount; + Surface.RepeatPreviewDeckCount = Weighting.RepeatPreviewDeckCount; + Surface.BroadenPreviewDeckCount = Weighting.BroadenPreviewDeckCount; + Surface.RedirectCoachGuidanceCount = Weighting.RedirectCoachGuidanceCount; + Surface.BroadenedLaunchCount = Weighting.BroadenedLaunchCount; + Surface.TightenedLaunchCount = Weighting.TightenedLaunchCount; + Surface.ClosureContinuationBias = Weighting.ClosureContinuationBias; + Surface.ClosureSuppressionBias = Weighting.ClosureSuppressionBias; + Surface.DeferredOverflowContinuationBias = + Weighting.DeferredOverflowContinuationBias; + Surface.DeferredOverflowCaseCount = Weighting.DeferredOverflowCaseCount; + Surface.ReliableAlignedTrustedTightPlanOutcomeHistoryCount = + Weighting.ReliableAlignedTrustedTightPlanOutcomeHistoryCount; + Surface.ReliableAlignedTrustedBroadPlanOutcomeHistoryCount = + Weighting.ReliableAlignedTrustedBroadPlanOutcomeHistoryCount; + Surface.ReliableAlignedTrustedTightContinuationDecisionCount = + Weighting.ReliableAlignedTrustedTightContinuationDecisionCount; + Surface.ReliableAlignedTrustedBroadRedirectDecisionCount = + Weighting.ReliableAlignedTrustedBroadRedirectDecisionCount; + Surface.AverageSuccessRate = Weighting.AverageSuccessRate; + Surface.AverageLaunchCaseCount = Weighting.AverageLaunchCaseCount; + Surface.BroadenedLaunchAverageSuccessRate = + Weighting.BroadenedLaunchAverageSuccessRate; + Surface.TightenedLaunchAverageSuccessRate = + Weighting.TightenedLaunchAverageSuccessRate; + Surface.ReliableAlignedTrustedTightPlanOutcomeAverageScore = + Weighting.ReliableAlignedTrustedTightPlanOutcomeAverageScore; + Surface.ReliableAlignedTrustedBroadPlanOutcomeAverageScore = + Weighting.ReliableAlignedTrustedBroadPlanOutcomeAverageScore; + Surface.bHasWeighting = Weighting.IsStructurallyValid(); + Surface.bHasWeightingOverride = Weighting.bHasOverride; + Surface.bUsedClosureHistorySignal = Weighting.bUsedClosureHistorySignal; + Surface.bUsedDeferredOverflowSignal = Weighting.bUsedDeferredOverflowSignal; + Surface.bUsedReliableAlignedPlanOutcomeSignal = + Weighting.bUsedReliableAlignedPlanOutcomeSignal; + + Surface.StatusLine = Weighting.IsStructurallyValid() + ? FString::Printf( + TEXT("Weighting: %d matching decisions | Base: %s | Weighted: %s | Last applied: %s"), + Weighting.MatchingDecisionCount, + *BuildGuidanceOutcomeActionLabel(Weighting.BaseAction), + *BuildGuidanceOutcomeActionLabel(Weighting.WeightedAction), + *BuildGuidanceOutcomeActionLabel(Weighting.LastAppliedAction)) + : TEXT("Weighting: no matching authored decision history yet; the current outcome action is recap-derived only."); + Surface.DetailLine = Weighting.IsStructurallyValid() + ? FString::Printf( + TEXT("Decision weighting for this lane/scope: repeat case %d | repeat deck %d | broaden %d | redirect %d | Avg success %.2f | Avg launch cases %.2f | broadened launches %d (avg %.2f) | tightened launches %d (avg %.2f) | Closure continue bias %d | Closure suppress bias %d | Deferred overflow bias %d | Deferred overflow cases %d | %s"), + Weighting.RepeatSelectedCaseCount, + Weighting.RepeatPreviewDeckCount, + Weighting.BroadenPreviewDeckCount, + Weighting.RedirectCoachGuidanceCount, + Weighting.AverageSuccessRate, + Weighting.AverageLaunchCaseCount, + Weighting.BroadenedLaunchCount, + Weighting.BroadenedLaunchAverageSuccessRate, + Weighting.TightenedLaunchCount, + Weighting.TightenedLaunchAverageSuccessRate, + Weighting.ClosureContinuationBias, + Weighting.ClosureSuppressionBias, + Weighting.DeferredOverflowContinuationBias, + Weighting.DeferredOverflowCaseCount, + Weighting.bHasOverride + ? (Weighting.bUsedDeferredOverflowSignal + ? TEXT("Deferred-overflow continuation history and authored choices adjusted the current guidance-outcome action.") + : (Weighting.bUsedClosureHistorySignal + ? TEXT("Recent closure-recovery history and authored choices adjusted the current guidance-outcome action.") + : TEXT("Recent authored choices adjusted the current guidance-outcome action."))) + : TEXT("Recent authored choices reinforced the recap-derived guidance-outcome action.")) + : TEXT("Weighting detail: apply outcome actions on the same guidance lane and scope to let the dashboard bias future recommendations."); + if (Weighting.ReliableAlignedTrustedTightPlanOutcomeHistoryCount > 0 + || Weighting.ReliableAlignedTrustedBroadPlanOutcomeHistoryCount > 0 + || Weighting.ReliableAlignedTrustedTightContinuationDecisionCount > 0 + || Weighting.ReliableAlignedTrustedBroadRedirectDecisionCount > 0) + { + Surface.DetailLine += FString::Printf( + TEXT(" | Aligned launched-plan trust: tight history %d (avg %.2f, continuation decisions %d) | broad history %d (avg %.2f, redirect decisions %d)%s"), + Weighting.ReliableAlignedTrustedTightPlanOutcomeHistoryCount, + Weighting.ReliableAlignedTrustedTightPlanOutcomeAverageScore, + Weighting.ReliableAlignedTrustedTightContinuationDecisionCount, + Weighting.ReliableAlignedTrustedBroadPlanOutcomeHistoryCount, + Weighting.ReliableAlignedTrustedBroadPlanOutcomeAverageScore, + Weighting.ReliableAlignedTrustedBroadRedirectDecisionCount, + Weighting.bUsedReliableAlignedPlanOutcomeSignal + ? TEXT(" | Aligned launched-plan trust adjusted the current guidance-outcome action.") + : TEXT("")); + } + if (Surface.bClosedLoopIdleActionability) + { + Surface.ResolvedActionLabel = TEXT("Loop Idle"); + Surface.StatusLine = + TEXT("Weighting: retained outcome history remains inspectable, but no live guidance-outcome action is active."); + Surface.DetailLine = + TEXT("Weighting detail: closed-loop idle preserves authored outcome history for review, but it is not surfacing another repeat, broaden, or redirect action."); + } + return Surface; +} + bool UHyperTwistCoachDashboardWidget::SelectPreviousMethodDrillFollowUpHistoryEntry() { if (MethodDrillFollowUpHistoryEntries.Num() <= 0) diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistCoachDashboardWidget.h b/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistCoachDashboardWidget.h index 3193d0e..d3d422f 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistCoachDashboardWidget.h +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistCoachDashboardWidget.h @@ -763,6 +763,18 @@ public: FHyperTwistTrainingCoachDashboardTemplateLaunchHistoryInspectSurface GetSelectedTemplateLaunchHistoryInspectSurface() const; + UFUNCTION(BlueprintPure, Category = "HyperTwist|Training|Coach|Dashboard") + FHyperTwistTrainingCoachDashboardGuidanceComparisonInspectSurface + GetDisplayedGuidanceComparisonInspectSurface() const; + + UFUNCTION(BlueprintPure, Category = "HyperTwist|Training|Coach|Dashboard") + FHyperTwistTrainingCoachDashboardGuidanceOutcomeInspectSurface + GetDisplayedGuidanceOutcomeInspectSurface() const; + + UFUNCTION(BlueprintPure, Category = "HyperTwist|Training|Coach|Dashboard") + FHyperTwistTrainingCoachDashboardGuidanceDecisionWeightingInspectSurface + GetDisplayedGuidanceDecisionWeightingInspectSurface() const; + UFUNCTION(BlueprintCallable, Category = "HyperTwist|Training|Coach|Dashboard") bool SelectPreviousMethodDrillFollowUpHistoryEntry(); diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingTypes.h b/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingTypes.h index cae3561..c54056a 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingTypes.h +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingTypes.h @@ -9729,6 +9729,368 @@ struct FHyperTwistTrainingCoachDashboardTemplateLaunchHistoryInspectSurface } }; +USTRUCT(BlueprintType) +struct FHyperTwistTrainingCoachDashboardGuidanceComparisonInspectSurface +{ + GENERATED_BODY() + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString Headline; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString StatusLine; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString DetailLine; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString RunRecapSourceLabel; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString TrainingSessionId; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString GuidanceLaneLabel; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString ScopeLabel; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString FocusLaneLabel; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString BriefHeadline; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString PrimaryActionLabel; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString PreviewDeckLabel; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString ActualDeckLabel; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString ExpectedModeLabel; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString ActualModeLabel; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString ExpectedSelectionLabel; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString ActualSelectionLabel; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString SelectedCaseId; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString LastCaseId; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + int32 ExpectedCaseCount = 0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + int32 FocusCaseCount = 0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + int32 CompletedAttemptCount = 0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + int32 FailureCount = 0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + int32 TimeoutCount = 0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + int32 DNFCount = 0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + int32 TotalMistakes = 0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + float SuccessRate = 0.0f; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bHasDisplayedRunRecap = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bHasDisplayedGuidanceBrief = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bHasDisplayedGuidanceDeck = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bHasComparisonContext = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bSingleCaseScope = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bSelectedCaseMatchesLastCase = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bClosedLoopIdleActionability = false; + + bool IsStructurallyValid() const + { + return !StatusLine.IsEmpty() + || !DetailLine.IsEmpty() + || bHasDisplayedRunRecap + || bHasComparisonContext; + } +}; + +USTRUCT(BlueprintType) +struct FHyperTwistTrainingCoachDashboardGuidanceOutcomeInspectSurface +{ + GENERATED_BODY() + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString Headline; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString StatusLine; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString DetailLine; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString RunRecapSourceLabel; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString TrainingSessionId; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString GuidanceLaneLabel; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString ScopeLabel; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString BriefHeadline; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString ActionLabel; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString BaseActionLabel; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString WeightedActionLabel; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString SelectedCaseId; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + int32 ExpectedCaseCount = 0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + int32 CompletedAttemptCount = 0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + int32 NonSuccessCount = 0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + int32 TotalMistakes = 0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + float SuccessRate = 0.0f; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bHasDisplayedRunRecap = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bHasDisplayedGuidanceBrief = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bHasDisplayedGuidanceDeck = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bHasComparisonContext = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bSingleCaseScope = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bCanApplyAction = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bCanRedirectAction = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bHasWeightingOverride = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bUsedClosureHistorySignal = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bUsedDeferredOverflowSignal = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bClosedLoopIdleActionability = false; + + bool IsStructurallyValid() const + { + return !StatusLine.IsEmpty() + || !DetailLine.IsEmpty() + || !ActionLabel.IsEmpty() + || bHasDisplayedRunRecap + || bHasComparisonContext; + } +}; + +USTRUCT(BlueprintType) +struct FHyperTwistTrainingCoachDashboardGuidanceDecisionWeightingInspectSurface +{ + GENERATED_BODY() + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString Headline; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString StatusLine; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString DetailLine; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString RunRecapSourceLabel; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString TrainingSessionId; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString GuidanceLaneLabel; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString ScopeLabel; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString BriefHeadline; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString SelectedCaseId; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString ResolvedActionLabel; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString BaseActionLabel; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString WeightedActionLabel; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString LastAppliedActionLabel; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + int32 MatchingDecisionCount = 0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + int32 RepeatSelectedCaseCount = 0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + int32 RepeatPreviewDeckCount = 0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + int32 BroadenPreviewDeckCount = 0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + int32 RedirectCoachGuidanceCount = 0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + int32 BroadenedLaunchCount = 0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + int32 TightenedLaunchCount = 0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + int32 ClosureContinuationBias = 0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + int32 ClosureSuppressionBias = 0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + int32 DeferredOverflowContinuationBias = 0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + int32 DeferredOverflowCaseCount = 0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + int32 ReliableAlignedTrustedTightPlanOutcomeHistoryCount = 0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + int32 ReliableAlignedTrustedBroadPlanOutcomeHistoryCount = 0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + int32 ReliableAlignedTrustedTightContinuationDecisionCount = 0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + int32 ReliableAlignedTrustedBroadRedirectDecisionCount = 0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + float AverageSuccessRate = 0.0f; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + float AverageLaunchCaseCount = 0.0f; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + float BroadenedLaunchAverageSuccessRate = 0.0f; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + float TightenedLaunchAverageSuccessRate = 0.0f; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + float ReliableAlignedTrustedTightPlanOutcomeAverageScore = 0.0f; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + float ReliableAlignedTrustedBroadPlanOutcomeAverageScore = 0.0f; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bHasDisplayedRunRecap = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bHasDisplayedGuidanceBrief = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bHasDisplayedGuidanceDeck = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bHasComparisonContext = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bSingleCaseScope = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bHasWeighting = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bHasWeightingOverride = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bUsedClosureHistorySignal = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bUsedDeferredOverflowSignal = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bUsedReliableAlignedPlanOutcomeSignal = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bClosedLoopIdleActionability = false; + + bool IsStructurallyValid() const + { + return !StatusLine.IsEmpty() + || !DetailLine.IsEmpty() + || bHasDisplayedRunRecap + || bHasComparisonContext + || bHasWeighting; + } +}; + USTRUCT(BlueprintType) struct FHyperTwistTrainingCoachGeneratedModeLaunchExecutionContextSurface { diff --git a/docs/HYPERTWIST_ROADMAP_OVERHAUL_EXPANSION_GUIDE.md b/docs/HYPERTWIST_ROADMAP_OVERHAUL_EXPANSION_GUIDE.md index 35f5254..2669def 100644 --- a/docs/HYPERTWIST_ROADMAP_OVERHAUL_EXPANSION_GUIDE.md +++ b/docs/HYPERTWIST_ROADMAP_OVERHAUL_EXPANSION_GUIDE.md @@ -69,8 +69,8 @@ Current correction call from the source-exposed audit and current execution refr - 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 now-complete deliberate `UHyperTwistTrainingRuntimeLibrary` world-context consumer lane reaches through active generated-mode selector option owned-execution split-phase current-selection merged ordered roster phase-id lookup, normalized-order lookup, and evaluation-order lookup inspect surfaces, so gameplay/Blueprint callers can inspect one retained merged-roster row directly by phase id, normalized order, or evaluation order without scanning the merged roster array or re-running inspect derivation details directly - - the latest landed bounded `Phase 4` packet stays on that same deliberate `UHyperTwistCoachDashboardWidget` retained-history inspect lane and adds selected guidance decision history and selected template launch history inspect surfaces over already-retained dashboard state, so gameplay/Blueprint callers can inspect authored decision retention, launch analytics fragments, and selection provenance without scraping dashboard text blocks or reopening decision-weighting or template-analytics derivation - - the dashboard retained-history inspect lane now appears functionally complete through queue-recovery outcome, queue-suppression history, closure-recovery history, method-drill recovery outcome history, method-drill recovery memory history, method-drill follow-up history, handoff history, guidance decision history, and template launch history inspect surfaces; the next clean move is to pivot deliberately to the live dashboard comparison / guidance-outcome / decision-weighting inspect surface trio rather than widening the retained-history seam by drift + - the latest landed bounded `Phase 4` packet pivots deliberately off that now-closed `UHyperTwistCoachDashboardWidget` retained-history inspect lane and adds displayed guidance comparison, displayed guidance outcome, and displayed guidance decision-weighting inspect surfaces over the live dashboard recap/comparison state and cached coach actionability state, so gameplay/Blueprint callers can inspect the active comparison target, current outcome action posture, and current authored weighting posture without scraping dashboard text blocks or reopening comparison/outcome derivation + - the next clean move is to stay on that same live dashboard guidance-consumer lane and land the active guidance preview selection, preview-case detail, rationale, and guidance-preference inspect surface group rather than drifting back into backend continuity or reopening the already-complete retained-history seam - 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_DASHBOARD_LIVE_GUIDANCE_COMPARISON_OUTCOME_DECISION_WEIGHTING_INSPECT_SURFACE_PACKET_2026-05-09.md b/docs/arch/HYPERTWIST_PHASE4_DASHBOARD_LIVE_GUIDANCE_COMPARISON_OUTCOME_DECISION_WEIGHTING_INSPECT_SURFACE_PACKET_2026-05-09.md new file mode 100644 index 0000000..4021b6f --- /dev/null +++ b/docs/arch/HYPERTWIST_PHASE4_DASHBOARD_LIVE_GUIDANCE_COMPARISON_OUTCOME_DECISION_WEIGHTING_INSPECT_SURFACE_PACKET_2026-05-09.md @@ -0,0 +1,54 @@ +# HyperTwist Phase 4 dashboard live guidance comparison, outcome, and decision-weighting inspect surface packet + +- bounded Phase `4` dashboard-consumer slice +- date: `2026-05-09` + +## Intent + +Pivot deliberately off the now-complete retained-history inspect lane and expose the live dashboard guidance comparison, guidance-outcome, and decision-weighting state directly to gameplay and Blueprint callers. + +## Why this packet exists + +The dashboard already derived: + +- the active recap-vs-preview comparison target +- the current repeat / broaden / redirect guidance-outcome posture +- the current authored decision-weighting bias over that same lane and scope + +But that live state still only surfaced through dashboard text blocks and the live action button state. Callers still had to scrape widget presentation or reopen comparison/outcome weighting derivation to inspect what the dashboard was currently showing. + +## What changed + +- added `FHyperTwistTrainingCoachDashboardGuidanceComparisonInspectSurface` +- added `FHyperTwistTrainingCoachDashboardGuidanceOutcomeInspectSurface` +- added `FHyperTwistTrainingCoachDashboardGuidanceDecisionWeightingInspectSurface` +- added `GetDisplayedGuidanceComparisonInspectSurface()` +- added `GetDisplayedGuidanceOutcomeInspectSurface()` +- added `GetDisplayedGuidanceDecisionWeightingInspectSurface()` +- kept the packet bounded to `UHyperTwistCoachDashboardWidget` plus shared surface types, with no dashboard redesign and no backend continuity changes + +## Files + +- `C:\HyperTwist\UnrealHyperTwist\Source\UnrealHyperTwist\Public\HyperTwistTraining\HyperTwistTrainingTypes.h` +- `C:\HyperTwist\UnrealHyperTwist\Source\UnrealHyperTwist\Public\HyperTwistTraining\HyperTwistCoachDashboardWidget.h` +- `C:\HyperTwist\UnrealHyperTwist\Source\UnrealHyperTwist\Private\HyperTwistTraining\HyperTwistCoachDashboardWidget.cpp` +- `C:\HyperTwist\docs\HYPERTWIST_ROADMAP_OVERHAUL_EXPANSION_GUIDE.md` + +## Outcome + +Gameplay and Blueprint callers can now inspect: + +- the currently displayed recap-vs-preview comparison target with lane, scope, preview-vs-actual deck/mode/selection posture, selected-case alignment, and closed-loop idle comparison posture +- the currently displayed guidance-outcome verdict with the live action button label, applyability posture, and closure-history / deferred-overflow adjustments +- the currently displayed decision-weighting summary with authored lane/scope bias, aligned launched-plan trust posture, and closed-loop idle weighting posture + +without scraping dashboard text blocks or reopening comparison/outcome/weighting derivation. + +## Validation + +- full `Development Editor | Win64` MSBuild on `UnrealHyperTwist.sln` + - passed with `0 Warning(s), 0 Error(s)` + +## Next clean slice + +Stay on the same live dashboard guidance-consumer lane and land the active guidance preview selection, preview-case detail, rationale, and guidance-preference inspect surface group.