diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistCoachDashboardWidget.cpp b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistCoachDashboardWidget.cpp index 958a7a4..c4bf67f 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistCoachDashboardWidget.cpp +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistCoachDashboardWidget.cpp @@ -8173,6 +8173,342 @@ UHyperTwistCoachDashboardWidget::GetSelectedHandoffHistoryInspectSurface() const return Surface; } +FHyperTwistTrainingCoachDashboardGuidanceDecisionHistoryInspectSurface +UHyperTwistCoachDashboardWidget::GetSelectedGuidanceDecisionHistoryInspectSurface() const +{ + FHyperTwistTrainingCoachDashboardGuidanceDecisionHistoryInspectSurface Surface; + Surface.Headline = TEXT("Decision history"); + Surface.SelectedEntryIndex = SelectedGuidanceDecisionIndex; + Surface.HistoryEntryCount = GuidanceDecisionHistoryEntries.Num(); + Surface.bClosedLoopIdleActionability = IsCoachLoopActionabilityClosed(); + + const auto BuildDecisionActionLabel = + [this](const EHyperTwistCoachDashboardOutcomeAction Action) -> FString + { + return Action == EHyperTwistCoachDashboardOutcomeAction::None + ? TEXT("none") + : BuildGuidanceOutcomeActionLabel(Action); + }; + + const EHyperTwistCoachDashboardOutcomeAction BaseAction = DeriveGuidanceOutcomeAction(); + const FHyperTwistCoachDashboardDecisionWeighting Weighting = + BuildGuidanceDecisionWeighting(BaseAction); + Surface.bHasWeighting = Weighting.IsStructurallyValid(); + 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.bHasWeightingOverride = Weighting.bHasOverride; + Surface.bUsedClosureHistorySignal = Weighting.bUsedClosureHistorySignal; + Surface.bUsedDeferredOverflowSignal = Weighting.bUsedDeferredOverflowSignal; + Surface.bUsedReliableAlignedPlanOutcomeSignal = + Weighting.bUsedReliableAlignedPlanOutcomeSignal; + Surface.BaseActionLabel = BuildDecisionActionLabel(Weighting.BaseAction); + Surface.WeightedActionLabel = BuildDecisionActionLabel(Weighting.WeightedAction); + Surface.LastAppliedActionLabel = BuildDecisionActionLabel(Weighting.LastAppliedAction); + Surface.WeightingStatusLine = 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.WeightingDetailLine = 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.WeightingDetailLine += 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.WeightingStatusLine = + TEXT("Weighting: retained outcome history remains inspectable, but no live guidance-outcome action is active."); + Surface.WeightingDetailLine = + TEXT("Weighting detail: closed-loop idle preserves authored outcome history for review, but it is not surfacing another repeat, broaden, or redirect action."); + } + + const FHyperTwistCoachDashboardDecisionHistoryEntry* SelectedEntry = + SelectedGuidanceDecisionIndex >= 0 + && SelectedGuidanceDecisionIndex < GuidanceDecisionHistoryEntries.Num() + ? &GuidanceDecisionHistoryEntries[SelectedGuidanceDecisionIndex] + : nullptr; + Surface.bHasSelectedEntry = + SelectedEntry != nullptr && SelectedEntry->IsStructurallyValid(); + if (!Surface.bHasSelectedEntry) + { + Surface.StatusLine = + TEXT("Decision history: no applied guidance-outcome decisions recorded yet."); + Surface.DetailLine = + TEXT("Decision detail: apply a guidance outcome from the dashboard to retain the authored choice history."); + if (Surface.bClosedLoopIdleActionability) + { + Surface.StatusLine = + TEXT("Decision history: no applied guidance-outcome decisions are selected; retained decision history remains inspectable in the closed-loop idle state."); + Surface.DetailLine = + TEXT("Decision detail: the coach-to-review loop is closed and idle, so no live guidance-outcome action is being surfaced for application."); + } + return Surface; + } + + Surface.RecordedAtUtc = SelectedEntry->RecordedAtUtc; + Surface.SourceSessionId = SelectedEntry->SourceSessionId; + Surface.GuidanceLaneLabel = + SelectedEntry->GuidanceLane == EHyperTwistCoachDashboardGuidancePreviewLane::Recommended + ? TEXT("recommended") + : (SelectedEntry->GuidanceLane + == EHyperTwistCoachDashboardGuidancePreviewLane::FollowUp + ? TEXT("follow-up") + : TEXT("none")); + Surface.ScopeLabel = + SelectedEntry->bSingleCaseScope ? TEXT("selected case") : TEXT("preview deck"); + Surface.BriefHeadline = + SelectedEntry->BriefHeadline.IsEmpty() ? TEXT("n/a") : SelectedEntry->BriefHeadline; + Surface.RecommendedActionLabel = SelectedEntry->RecommendedActionLabel; + Surface.AppliedActionLabel = SelectedEntry->AppliedActionLabel; + Surface.SelectedCaseId = + SelectedEntry->SelectedCaseId.IsEmpty() ? TEXT("n/a") : SelectedEntry->SelectedCaseId; + Surface.LaunchBudgetReasonLine = + SelectedEntry->LaunchBudgetReasonLine.IsEmpty() + ? TEXT("n/a") + : SelectedEntry->LaunchBudgetReasonLine; + Surface.CompletedAttemptCount = SelectedEntry->CompletedAttemptCount; + Surface.TotalMistakes = SelectedEntry->TotalMistakes; + Surface.LaunchedCaseCount = SelectedEntry->LaunchedCaseCount; + Surface.ReferenceCaseCount = SelectedEntry->ReferenceCaseCount; + Surface.SuccessRate = SelectedEntry->SuccessRate; + Surface.bSingleCaseScope = SelectedEntry->bSingleCaseScope; + Surface.bBroadenedLaunchScope = SelectedEntry->bBroadenedLaunchScope; + Surface.bTightenedLaunchScope = SelectedEntry->bTightenedLaunchScope; + Surface.bUsedReliableAlignedTrustedTightPlanOutcomeHistory = + SelectedEntry->bUsedReliableAlignedTrustedTightPlanOutcomeHistory; + Surface.bUsedReliableAlignedTrustedBroadPlanOutcomeHistory = + SelectedEntry->bUsedReliableAlignedTrustedBroadPlanOutcomeHistory; + Surface.StatusLine = FString::Printf( + TEXT("Decision history: %d entries | selected %d/%d | Lane: %s | Scope: %s | Recorded: %s"), + GuidanceDecisionHistoryEntries.Num(), + SelectedGuidanceDecisionIndex + 1, + GuidanceDecisionHistoryEntries.Num(), + *Surface.GuidanceLaneLabel, + *Surface.ScopeLabel, + *SelectedEntry->RecordedAtUtc); + Surface.DetailLine = FString::Printf( + TEXT("Session: %s | Brief: %s | Recommended: %s | Applied: %s | Selected case: %s | Launch cases: %d/%d | Scope: %s | Success rate: %.2f | Completed attempts: %d | Mistakes: %d | Budget reason: %s"), + *SelectedEntry->SourceSessionId, + *Surface.BriefHeadline, + *SelectedEntry->RecommendedActionLabel, + *SelectedEntry->AppliedActionLabel, + *Surface.SelectedCaseId, + SelectedEntry->LaunchedCaseCount, + SelectedEntry->ReferenceCaseCount, + SelectedEntry->bBroadenedLaunchScope + ? TEXT("broadened") + : (SelectedEntry->bTightenedLaunchScope + ? TEXT("tightened") + : TEXT("steady")), + SelectedEntry->SuccessRate, + SelectedEntry->CompletedAttemptCount, + SelectedEntry->TotalMistakes, + *Surface.LaunchBudgetReasonLine); + if (SelectedEntry->ReliableAlignedTrustedTightPlanOutcomeHistoryCount > 0 + || SelectedEntry->ReliableAlignedTrustedBroadPlanOutcomeHistoryCount > 0 + || SelectedEntry->bUsedReliableAlignedTrustedTightPlanOutcomeHistory + || SelectedEntry->bUsedReliableAlignedTrustedBroadPlanOutcomeHistory) + { + Surface.DetailLine += FString::Printf( + TEXT(" | Aligned launched-plan trust: tight history %d (avg %.2f) | broad history %d (avg %.2f)%s"), + SelectedEntry->ReliableAlignedTrustedTightPlanOutcomeHistoryCount, + SelectedEntry->ReliableAlignedTrustedTightPlanOutcomeAverageScore, + SelectedEntry->ReliableAlignedTrustedBroadPlanOutcomeHistoryCount, + SelectedEntry->ReliableAlignedTrustedBroadPlanOutcomeAverageScore, + SelectedEntry->bUsedReliableAlignedTrustedTightPlanOutcomeHistory + ? TEXT(" | This decision was taken while tight aligned launched-plan trust was active.") + : (SelectedEntry->bUsedReliableAlignedTrustedBroadPlanOutcomeHistory + ? TEXT(" | This decision was taken while broad aligned launched-plan redirect trust was active.") + : TEXT(""))); + } + return Surface; +} + +FHyperTwistTrainingCoachDashboardTemplateLaunchHistoryInspectSurface +UHyperTwistCoachDashboardWidget::GetSelectedTemplateLaunchHistoryInspectSurface() const +{ + FHyperTwistTrainingCoachDashboardTemplateLaunchHistoryInspectSurface Surface; + Surface.Headline = TEXT("Template launch history"); + Surface.SelectedEntryIndex = SelectedTemplateLaunchHistoryIndex; + Surface.HistoryEntryCount = TemplateLaunchHistoryEntries.Num(); + + const FHyperTwistCoachDashboardTemplateLaunchHistoryEntry* SelectedEntry = + SelectedTemplateLaunchHistoryIndex >= 0 + && SelectedTemplateLaunchHistoryIndex < TemplateLaunchHistoryEntries.Num() + ? &TemplateLaunchHistoryEntries[SelectedTemplateLaunchHistoryIndex] + : nullptr; + Surface.bHasSelectedEntry = + SelectedEntry != nullptr && SelectedEntry->IsStructurallyValid(); + if (!Surface.bHasSelectedEntry) + { + Surface.StatusLine = + TEXT("Template launch history: no completed template-backed runs retained yet."); + Surface.DetailLine = + TEXT("Template launch history detail: completed runs started through the dashboard template lane will accumulate here with template id, template kind, and source review-plan provenance."); + return Surface; + } + + const FHyperTwistTrainingTemplateLaunchScopedStats* SelectedStats = + FindCachedTemplateLaunchScopedStats( + SelectedEntry->TemplateId, + SelectedEntry->UserId); + Surface.bHasScopedStats = + SelectedStats != nullptr && SelectedStats->IsStructurallyValid(); + Surface.bHasSelectionProvenance = + SelectedEntry->SelectionProvenance.IsStructurallyValid(); + Surface.SelectionProvenance = Surface.bHasSelectionProvenance + ? SelectedEntry->SelectionProvenance + : FHyperTwistTrainingTemplateLaunchSelectionProvenance(); + Surface.AnalyticsOutcomeLine = + HyperTwistCoachDashboardWidgetInternal::BuildTemplateLaunchAnalyticsOutcomeFragment( + SelectedStats); + Surface.AnalyticsDetailLine = + HyperTwistCoachDashboardWidgetInternal::BuildTemplateLaunchAnalyticsDetailFragment( + SelectedStats); + Surface.SelectionDetailLine = + HyperTwistCoachDashboardWidgetInternal::BuildTemplateLaunchSelectionDetailFragment( + Surface.bHasSelectionProvenance ? &SelectedEntry->SelectionProvenance : nullptr); + Surface.RecordedAtUtc = SelectedEntry->RecordedAtUtc; + Surface.CompletedAtUtc = + SelectedEntry->CompletedAtUtc.IsEmpty() ? TEXT("n/a") : SelectedEntry->CompletedAtUtc; + Surface.SourceSessionId = SelectedEntry->SourceSessionId; + Surface.TemplateId = + SelectedEntry->TemplateId.IsEmpty() ? TEXT("n/a") : SelectedEntry->TemplateId; + Surface.TemplateTitle = + SelectedEntry->TemplateTitle.IsEmpty() ? TEXT("n/a") : SelectedEntry->TemplateTitle; + Surface.UserId = SelectedEntry->UserId.IsEmpty() ? TEXT("n/a") : SelectedEntry->UserId; + Surface.FocusDeckId = + SelectedEntry->FocusDeckId.IsEmpty() ? TEXT("n/a") : SelectedEntry->FocusDeckId; + Surface.SourceReviewPlanId = + SelectedEntry->SourceReviewPlanId.IsEmpty() + ? TEXT("none") + : SelectedEntry->SourceReviewPlanId; + Surface.TemplateKind = SelectedEntry->TemplateKind; + Surface.TemplateKindLabel = + HyperTwistCoachDashboardWidgetInternal::EnumDisplayName( + SelectedEntry->TemplateKind); + Surface.SessionState = SelectedEntry->SessionState; + Surface.SessionStateLabel = + HyperTwistCoachDashboardWidgetInternal::EnumDisplayName( + SelectedEntry->SessionState); + Surface.SuggestedMode = SelectedEntry->SuggestedMode; + Surface.SuggestedModeLabel = + HyperTwistCoachDashboardWidgetInternal::EnumDisplayName( + SelectedEntry->SuggestedMode); + Surface.SuggestedSelectionPolicy = SelectedEntry->SuggestedSelectionPolicy; + Surface.SuggestedSelectionLabel = + HyperTwistCoachDashboardWidgetInternal::EnumDisplayName( + SelectedEntry->SuggestedSelectionPolicy); + Surface.RecommendedCaseCount = SelectedEntry->RecommendedCaseCount; + Surface.FocusCaseCount = SelectedEntry->FocusCaseCount; + Surface.CompletedAttemptCount = SelectedEntry->CompletedAttemptCount; + Surface.TotalMistakes = SelectedEntry->TotalMistakes; + Surface.SuccessRate = SelectedEntry->SuccessRate; + Surface.bPreferReviewRun = SelectedEntry->bPreferReviewRun; + Surface.bCoachReview = SelectedEntry->bCoachReview; + Surface.bPreferShortReviewSet = SelectedEntry->bPreferShortReviewSet; + Surface.StatusLine = FString::Printf( + TEXT("Template launch history: %d entries | selected %d/%d | Template: %s | State: %s | Completed: %s | Aggregate: %s"), + TemplateLaunchHistoryEntries.Num(), + SelectedTemplateLaunchHistoryIndex + 1, + TemplateLaunchHistoryEntries.Num(), + *Surface.TemplateId, + *Surface.SessionStateLabel, + *Surface.CompletedAtUtc, + *Surface.AnalyticsOutcomeLine); + Surface.DetailLine = FString::Printf( + TEXT("Title: %s | kind: %s | mode: %s | selection: %s | deck: %s | user: %s | source plan: %s | recommended %d | focus %d | attempts %d | success %.2f | mistakes %d | review-backed: %s | coach-review: %s | short set: %s | Aggregate detail: %s"), + *Surface.TemplateTitle, + *Surface.TemplateKindLabel, + *Surface.SuggestedModeLabel, + *Surface.SuggestedSelectionLabel, + *Surface.FocusDeckId, + *Surface.UserId, + *Surface.SourceReviewPlanId, + SelectedEntry->RecommendedCaseCount, + SelectedEntry->FocusCaseCount, + SelectedEntry->CompletedAttemptCount, + SelectedEntry->SuccessRate, + SelectedEntry->TotalMistakes, + SelectedEntry->bPreferReviewRun ? TEXT("yes") : TEXT("no"), + SelectedEntry->bCoachReview ? TEXT("yes") : TEXT("no"), + SelectedEntry->bPreferShortReviewSet ? TEXT("yes") : TEXT("no"), + *Surface.AnalyticsDetailLine); + if (!Surface.SelectionDetailLine.IsEmpty()) + { + Surface.DetailLine += TEXT(" | "); + Surface.DetailLine += Surface.SelectionDetailLine; + } + 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 f4e835f..3193d0e 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistCoachDashboardWidget.h +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistCoachDashboardWidget.h @@ -755,6 +755,14 @@ public: FHyperTwistTrainingCoachDashboardHandoffHistoryInspectSurface GetSelectedHandoffHistoryInspectSurface() const; + UFUNCTION(BlueprintPure, Category = "HyperTwist|Training|Coach|Dashboard") + FHyperTwistTrainingCoachDashboardGuidanceDecisionHistoryInspectSurface + GetSelectedGuidanceDecisionHistoryInspectSurface() const; + + UFUNCTION(BlueprintPure, Category = "HyperTwist|Training|Coach|Dashboard") + FHyperTwistTrainingCoachDashboardTemplateLaunchHistoryInspectSurface + GetSelectedTemplateLaunchHistoryInspectSurface() 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 53f2059..cae3561 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingTypes.h +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingTypes.h @@ -9415,6 +9415,320 @@ struct FHyperTwistTrainingCoachDashboardHandoffHistoryInspectSurface } }; +USTRUCT(BlueprintType) +struct FHyperTwistTrainingCoachDashboardGuidanceDecisionHistoryInspectSurface +{ + 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 WeightingStatusLine; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString WeightingDetailLine; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString RecordedAtUtc; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString SourceSessionId; + + 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 RecommendedActionLabel; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString AppliedActionLabel; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString SelectedCaseId; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString LaunchBudgetReasonLine; + + 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 SelectedEntryIndex = INDEX_NONE; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + int32 HistoryEntryCount = 0; + + 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") + int32 CompletedAttemptCount = 0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + int32 TotalMistakes = 0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + int32 LaunchedCaseCount = 0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + int32 ReferenceCaseCount = 0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + float SuccessRate = 0.0f; + + 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 bHasSelectedEntry = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bHasWeighting = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bSingleCaseScope = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bBroadenedLaunchScope = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bTightenedLaunchScope = 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 bUsedReliableAlignedTrustedTightPlanOutcomeHistory = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bUsedReliableAlignedTrustedBroadPlanOutcomeHistory = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bClosedLoopIdleActionability = false; + + bool IsStructurallyValid() const + { + return !StatusLine.IsEmpty() + || !DetailLine.IsEmpty() + || !WeightingStatusLine.IsEmpty() + || !WeightingDetailLine.IsEmpty() + || bHasSelectedEntry + || bHasWeighting + || HistoryEntryCount > 0; + } +}; + +USTRUCT(BlueprintType) +struct FHyperTwistTrainingCoachDashboardTemplateLaunchHistoryInspectSurface +{ + 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 AnalyticsOutcomeLine; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString AnalyticsDetailLine; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString SelectionDetailLine; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString RecordedAtUtc; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString CompletedAtUtc; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString SourceSessionId; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString TemplateId; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString TemplateTitle; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString UserId; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString FocusDeckId; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString SourceReviewPlanId; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString TemplateKindLabel; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString SessionStateLabel; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString SuggestedModeLabel; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString SuggestedSelectionLabel; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + int32 SelectedEntryIndex = INDEX_NONE; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + int32 HistoryEntryCount = 0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + int32 RecommendedCaseCount = 0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + int32 FocusCaseCount = 0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + int32 CompletedAttemptCount = 0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + int32 TotalMistakes = 0; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + float SuccessRate = 0.0f; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FHyperTwistTrainingTemplateLaunchSelectionProvenance SelectionProvenance; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + EHyperTwistTrainingSessionTemplateKind TemplateKind = + EHyperTwistTrainingSessionTemplateKind::Recovery; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + EHyperTwistTrainingSessionState SessionState = + EHyperTwistTrainingSessionState::Created; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + EHyperTwistTrainingDeliveryMode SuggestedMode = + EHyperTwistTrainingDeliveryMode::Timer; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + EHyperTwistTrainingSelectionPolicy SuggestedSelectionPolicy = + EHyperTwistTrainingSelectionPolicy::Weighted; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bHasSelectedEntry = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bHasScopedStats = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bHasSelectionProvenance = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bPreferReviewRun = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bCoachReview = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bPreferShortReviewSet = false; + + bool IsStructurallyValid() const + { + return !StatusLine.IsEmpty() + || !DetailLine.IsEmpty() + || bHasSelectedEntry + || HistoryEntryCount > 0; + } +}; + USTRUCT(BlueprintType) struct FHyperTwistTrainingCoachGeneratedModeLaunchExecutionContextSurface { diff --git a/docs/HYPERTWIST_ROADMAP_OVERHAUL_EXPANSION_GUIDE.md b/docs/HYPERTWIST_ROADMAP_OVERHAUL_EXPANSION_GUIDE.md index 8576bd0..35f5254 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 method-drill follow-up history and selected handoff history inspect surfaces over already-retained dashboard state, so gameplay/Blueprint callers can inspect retained drill follow-up recap posture and retained handoff-pattern shifts without scraping dashboard text blocks or reopening follow-up weighting or handoff-memory derivation - - the current next bounded packet should stay on that same dashboard retained-history inspect lane only while a real gameplay or Blueprint caller still needs one more thin inspect contract over already-retained dashboard history state; the next clean move inside that lane is the guidance decision history and template launch history inspect pair rather than widening back into backend continuity or runtime-library drift + - 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 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_GUIDANCE_DECISION_TEMPLATE_LAUNCH_HISTORY_INSPECT_SURFACE_PACKET_2026-05-09.md b/docs/arch/HYPERTWIST_PHASE4_DASHBOARD_GUIDANCE_DECISION_TEMPLATE_LAUNCH_HISTORY_INSPECT_SURFACE_PACKET_2026-05-09.md new file mode 100644 index 0000000..b2ebf98 --- /dev/null +++ b/docs/arch/HYPERTWIST_PHASE4_DASHBOARD_GUIDANCE_DECISION_TEMPLATE_LAUNCH_HISTORY_INSPECT_SURFACE_PACKET_2026-05-09.md @@ -0,0 +1,50 @@ +# HyperTwist Phase 4 dashboard guidance decision and template launch history inspect surface packet + +- bounded Phase `4` dashboard-consumer slice +- date: `2026-05-09` + +## Intent + +Stay on the deliberate `UHyperTwistCoachDashboardWidget` retained-history inspect lane and expose the last two retained dashboard history families directly to gameplay and Blueprint callers. + +## Why this packet exists + +The dashboard already retained: + +- selected guidance decision history entries and their current decision-weighting posture +- selected template launch history entries plus retained template-launch analytics and selection provenance + +But that state still only surfaced through dashboard text blocks and selection actions. Callers still had to scrape widget presentation or reopen decision-weighting and template-analytics derivation to inspect retained orchestration history directly. + +## What changed + +- added `FHyperTwistTrainingCoachDashboardGuidanceDecisionHistoryInspectSurface` +- added `FHyperTwistTrainingCoachDashboardTemplateLaunchHistoryInspectSurface` +- added `GetSelectedGuidanceDecisionHistoryInspectSurface()` +- added `GetSelectedTemplateLaunchHistoryInspectSurface()` +- 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 selected retained guidance decision entry with lane, scope, recommended vs applied action, launch scope/budget posture, aligned launched-plan trust posture, and the current decision-weighting summary that still biases that lane and scope +- the selected retained template launch entry with template/session posture, retained aggregate analytics fragments, and retained selection provenance without reopening the scoped-stats derivation path + +without scraping dashboard text blocks or reopening decision-weighting or template-analytics derivation. + +## Validation + +- full `Development Editor | Win64` MSBuild on `UnrealHyperTwist.sln` + - passed with `0 Warning(s), 0 Error(s)` + +## Next clean slice + +The dashboard retained-history inspect lane now appears functionally complete. The next clean move is to pivot deliberately to the live dashboard comparison / guidance-outcome / decision-weighting inspect surface trio.