From 094d1c197553c1dfdc8f002ed0888f16ead2e68b Mon Sep 17 00:00:00 2001 From: axiomlogicnexus Date: Mon, 27 Apr 2026 17:58:09 +0200 Subject: [PATCH] Feed queue recovery into coach memory --- .../HyperTwistTrainingRepositoryLibrary.cpp | 113 ++++++++++++++++++ 1 file changed, 113 insertions(+) diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingRepositoryLibrary.cpp b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingRepositoryLibrary.cpp index 584b3db..ca6d88b 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingRepositoryLibrary.cpp +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingRepositoryLibrary.cpp @@ -237,6 +237,47 @@ namespace HyperTwistTrainingRepositoryLibraryInternal } } + EHyperTwistTrainingCoachGuidanceLane GuidanceLaneFromQueueSourceLabel(const FString& SourceLabel) + { + if (SourceLabel == TEXT("follow-up-brief")) + { + return EHyperTwistTrainingCoachGuidanceLane::FollowUp; + } + if (SourceLabel == TEXT("primary-brief")) + { + return EHyperTwistTrainingCoachGuidanceLane::Recommended; + } + + return EHyperTwistTrainingCoachGuidanceLane::None; + } + + EHyperTwistTrainingCoachGuidanceLane GuidanceLaneFromQueueEntry( + const FHyperTwistTrainingCoachSessionQueueStateEntry* Entry) + { + if (Entry == nullptr) + { + return EHyperTwistTrainingCoachGuidanceLane::None; + } + if (Entry->bRequiresFollowUp) + { + return EHyperTwistTrainingCoachGuidanceLane::FollowUp; + } + + return GuidanceLaneFromQueueSourceLabel(Entry->SourceLabel); + } + + const FHyperTwistTrainingCoachSessionQueueStateEntry* FindQueueEntryById( + const FHyperTwistTrainingCoachSessionQueueState& QueueState, + const FString& EntryId) + { + return QueueState.Entries.FindByPredicate( + [&EntryId](const FHyperTwistTrainingCoachSessionQueueStateEntry& Candidate) + { + return Candidate.EntryId == EntryId; + } + ); + } + int32 ComputePlanDurationMs( const FHyperTwistTrainingReviewPlanState& ReviewPlan, const FString& ReferenceUtc @@ -5138,6 +5179,61 @@ FHyperTwistTrainingCoachMemorySnapshot UHyperTwistTrainingRepositoryLibrary::Der || QueueExecutionSummary.bHasBlockedFollowUpFriction || QueueExecutionSummary.bHasRepeatManualRescheduleFriction; + const FHyperTwistTrainingCoachSessionQueueStateEntry* MostDeferredQueueEntry = + HyperTwistTrainingRepositoryLibraryInternal::FindQueueEntryById( + ActiveQueueState, + QueueExecutionSummary.MostDeferredEntryId + ); + const FHyperTwistTrainingCoachSessionQueueStateEntry* MostCompletedQueueEntry = + HyperTwistTrainingRepositoryLibraryInternal::FindQueueEntryById( + ActiveQueueState, + QueueExecutionSummary.MostCompletedEntryId + ); + const EHyperTwistTrainingCoachGuidanceLane DominantDeferredLane = + HyperTwistTrainingRepositoryLibraryInternal::GuidanceLaneFromQueueEntry( + MostDeferredQueueEntry + ); + const EHyperTwistTrainingCoachGuidanceLane DominantCompletedLane = + HyperTwistTrainingRepositoryLibraryInternal::GuidanceLaneFromQueueEntry( + MostCompletedQueueEntry + ); + const int32 DeferredRecoverySignal = FMath::Clamp( + (QueueExecutionSummary.DeferredEventCount / 2) + + (QueueExecutionSummary.ManualRescheduleEventCount > 0 ? 1 : 0), + 0, + 3 + ); + const int32 PromotionRecoverySignal = FMath::Clamp( + QueueExecutionSummary.PromotedEventCount + + (QueueExecutionSummary.CompletedEventCount > 0 ? 1 : 0), + 0, + 3 + ); + const bool bQueueHistoryPrefersFollowUp = + DominantDeferredLane == EHyperTwistTrainingCoachGuidanceLane::FollowUp + || (DominantCompletedLane == EHyperTwistTrainingCoachGuidanceLane::FollowUp + && QueueExecutionSummary.PromotedEventCount > 0); + const bool bQueueHistoryPrefersRecommended = + DominantDeferredLane == EHyperTwistTrainingCoachGuidanceLane::Recommended + || (DominantCompletedLane == EHyperTwistTrainingCoachGuidanceLane::Recommended + && QueueExecutionSummary.PromotedEventCount > 0); + + if (bQueueHistoryPrefersFollowUp) + { + Snapshot.FollowUpGuidanceContinueBias += FMath::Max( + 1, + DeferredRecoverySignal + PromotionRecoverySignal + ); + } + if (bQueueHistoryPrefersRecommended) + { + Snapshot.RecommendedGuidanceContinueBias += FMath::Max( + 1, + DeferredRecoverySignal + PromotionRecoverySignal + - (QueueExecutionSummary.bHasBlockedFollowUpFriction ? 1 : 0) + ); + } + if (!QueueExecutionSummary.FocusDeckId.IsEmpty() && (Snapshot.FocusDeckId.IsEmpty() || Snapshot.bNeedsScheduleFrictionRecovery)) { @@ -5221,6 +5317,23 @@ FHyperTwistTrainingCoachMemorySnapshot UHyperTwistTrainingRepositoryLibrary::Der || QueueExecutionSummary.bHasOutstandingDeferredWork || QueueExecutionSummary.bHasCompletionGap || QueueExecutionSummary.bHasRepeatManualRescheduleFriction; + + if (bQueueHistoryPrefersFollowUp + && (QueueExecutionSummary.PromotedEventCount > 0 + || QueueExecutionSummary.bHasBlockedFollowUpFriction + || QueueExecutionSummary.BlockedFollowUpEventCount > 0)) + { + Snapshot.PreferredGuidanceLane = EHyperTwistTrainingCoachGuidanceLane::FollowUp; + Snapshot.PreferredGuidanceSourceLabel = TEXT("coach-memory-queue-follow-up"); + } + else if (bQueueHistoryPrefersRecommended + && Snapshot.PreferredGuidanceLane != EHyperTwistTrainingCoachGuidanceLane::FollowUp + && (QueueExecutionSummary.PromotedEventCount > 0 + || QueueExecutionSummary.CompletedEventCount > 0)) + { + Snapshot.PreferredGuidanceLane = EHyperTwistTrainingCoachGuidanceLane::Recommended; + Snapshot.PreferredGuidanceSourceLabel = TEXT("coach-memory-queue-recommended"); + } } }