From 602abb8de920e8804da172dcb5bcd93148be5bf3 Mon Sep 17 00:00:00 2001 From: axiomlogicnexus Date: Mon, 27 Apr 2026 20:16:12 +0200 Subject: [PATCH] Explain closure policy in schedule summary --- .../HyperTwistContractLibrary.cpp | 1 + .../HyperTwistCoachDashboardWidget.cpp | 7 +++- .../HyperTwistTrainingCoachLibrary.cpp | 38 +++++++++++++++++++ .../HyperTwistTrainingSubsystem.cpp | 1 + .../HyperTwistTrainingCoachLibrary.h | 10 +++++ 5 files changed, 55 insertions(+), 2 deletions(-) diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistBootstrap/HyperTwistContractLibrary.cpp b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistBootstrap/HyperTwistContractLibrary.cpp index 0583307..14ff531 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistBootstrap/HyperTwistContractLibrary.cpp +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistBootstrap/HyperTwistContractLibrary.cpp @@ -1542,6 +1542,7 @@ FHyperTwistCoachSchedulePolicySummary UHyperTwistContractLibrary::MakeSampleTrai return UHyperTwistTrainingCoachLibrary::DeriveCoachSchedulePolicySummary( MakeSampleTrainingCoachSessionQueueState(), MakeSampleTrainingCoachScheduleHorizonSummary(), + MakeSampleTrainingCoachActionClosureSummary(), MakeSampleTrainingCoachArchivePolicySummary(), TEXT("2026-04-28T12:30:00Z") ); diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistCoachDashboardWidget.cpp b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistCoachDashboardWidget.cpp index 920eeab..dda24dd 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistCoachDashboardWidget.cpp +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistCoachDashboardWidget.cpp @@ -5279,7 +5279,7 @@ void UHyperTwistCoachDashboardWidget::UpdateDashboardPresentation() if (SchedulePolicyTextBlock != nullptr) { SchedulePolicyTextBlock->SetText(FText::FromString(FString::Printf( - TEXT("Policy: immediate %d | pending %d | deferred %d | manual %d | earliest deferred: %s | dominant reason: %s | pressure %.2f | manual allowed: %s | expired deferrals: %s | review needed: %s | archive-aware: %s | blocked follow-up: %s"), + TEXT("Policy: immediate %d | pending %d | deferred %d | manual %d | earliest deferred: %s | dominant reason: %s | pressure %.2f | manual allowed: %s | expired deferrals: %s | review needed: %s | archive-aware: %s | blocked follow-up: %s | closure: %s"), CachedCoachSchedulePolicySummary.ImmediateEntryCount, CachedCoachSchedulePolicySummary.PendingEntryCount, CachedCoachSchedulePolicySummary.DeferredEntryCount, @@ -5295,7 +5295,10 @@ void UHyperTwistCoachDashboardWidget::UpdateDashboardPresentation() CachedCoachSchedulePolicySummary.bHasExpiredDeferrals ? TEXT("yes") : TEXT("no"), CachedCoachSchedulePolicySummary.bNeedsManualRescheduleReview ? TEXT("yes") : TEXT("no"), CachedCoachSchedulePolicySummary.bHasArchiveAwareDeferrals ? TEXT("yes") : TEXT("no"), - CachedCoachSchedulePolicySummary.bHasBlockedFollowUpDeferrals ? TEXT("yes") : TEXT("no") + CachedCoachSchedulePolicySummary.bHasBlockedFollowUpDeferrals ? TEXT("yes") : TEXT("no"), + CachedCoachSchedulePolicySummary.ClosurePolicyLine.IsEmpty() + ? TEXT("n/a") + : *CachedCoachSchedulePolicySummary.ClosurePolicyLine ))); } if (QueueRecoveryHeaderTextBlock != nullptr) diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingCoachLibrary.cpp b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingCoachLibrary.cpp index 7c93324..f1e2a17 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingCoachLibrary.cpp +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingCoachLibrary.cpp @@ -1593,6 +1593,7 @@ FHyperTwistCoachScheduleHorizonSummary UHyperTwistTrainingCoachLibrary::DeriveCo FHyperTwistCoachSchedulePolicySummary UHyperTwistTrainingCoachLibrary::DeriveCoachSchedulePolicySummary( const FHyperTwistTrainingCoachSessionQueueState& QueueState, const FHyperTwistCoachScheduleHorizonSummary& HorizonSummary, + const FHyperTwistTrainingCoachActionClosureSummary& ClosureSummary, const FHyperTwistTrainingCoachArchivePolicySummary& ArchivePolicySummary, const FString& ReferenceUtc ) @@ -1695,9 +1696,46 @@ FHyperTwistCoachSchedulePolicySummary UHyperTwistTrainingCoachLibrary::DeriveCoa Summary.bHasArchiveAwareDeferrals = Summary.ArchiveAwareDeferredEntryCount > 0 || (ArchivePolicySummary.bNeedsArchiveRetentionTuning && Summary.DeferredEntryCount > 0); Summary.bHasBlockedFollowUpDeferrals = Summary.BlockedFollowUpDeferredEntryCount > 0; + Summary.bFollowUpContinuationFromRecoveryHistory = + ClosureSummary.bSpawnFollowUpFromSuppressionMemory; + Summary.bBorderlineFollowUpSuppressedByRecoveryHistory = + ClosureSummary.bSuppressedBorderlineFollowUpByRecoveryHistory; Summary.bNeedsManualRescheduleReview = Summary.bHasExpiredDeferrals || Summary.ManualRescheduleEntryCount >= 2 || Summary.bHasBlockedFollowUpDeferrals; + if (ClosureSummary.IsStructurallyValid()) + { + if (Summary.bFollowUpContinuationFromRecoveryHistory) + { + Summary.ClosurePolicyLine = + TEXT("Closure policy: recovery history keeps follow-up alive on carry-forward pressure."); + } + else if (Summary.bBorderlineFollowUpSuppressedByRecoveryHistory) + { + Summary.ClosurePolicyLine = + TEXT("Closure policy: recommended recovery history suppresses a borderline follow-up."); + } + else if (ClosureSummary.bShouldSpawnFollowUp) + { + Summary.ClosurePolicyLine = + TEXT("Closure policy: unresolved or weak cases still require follow-up."); + } + else if (ClosureSummary.bNeedsEscalation) + { + Summary.ClosurePolicyLine = + TEXT("Closure policy: escalation takes precedence over additional follow-up."); + } + else if (ClosureSummary.bSequenceClosed) + { + Summary.ClosurePolicyLine = + TEXT("Closure policy: sequence is closed and resolved work can archive forward."); + } + else + { + Summary.ClosurePolicyLine = + TEXT("Closure policy: sequence remains stable without spawning new follow-up."); + } + } Summary.ReschedulePressureScore = FMath::Clamp( static_cast(Summary.DeferredEntryCount) * 8.0f + static_cast(Summary.ManualRescheduleEntryCount) * 10.0f diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingSubsystem.cpp b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingSubsystem.cpp index eadab4d..cf358a0 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingSubsystem.cpp +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingSubsystem.cpp @@ -2302,6 +2302,7 @@ void UHyperTwistTrainingSubsystem::RefreshRepositoryViews() ActiveCoachSchedulePolicySummary = UHyperTwistTrainingCoachLibrary::DeriveCoachSchedulePolicySummary( ActiveCoachSessionQueueState, ActiveCoachScheduleHorizonSummary, + ActiveCoachActionClosureSummary, ActiveCoachArchivePolicySummary, ActiveLearnerDeckStateSummary.ReferenceUtc ); diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingCoachLibrary.h b/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingCoachLibrary.h index 297761e..51e02fe 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingCoachLibrary.h +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingCoachLibrary.h @@ -361,6 +361,9 @@ struct FHyperTwistCoachSchedulePolicySummary UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") FString DominantDeferralReasonLabel; + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + FString ClosurePolicyLine; + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") int32 ImmediateEntryCount = 0; @@ -397,6 +400,12 @@ struct FHyperTwistCoachSchedulePolicySummary UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") bool bHasBlockedFollowUpDeferrals = false; + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bFollowUpContinuationFromRecoveryHistory = false; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bBorderlineFollowUpSuppressedByRecoveryHistory = false; + bool IsStructurallyValid() const { return !UserId.IsEmpty() && !QueueId.IsEmpty(); @@ -474,6 +483,7 @@ public: static FHyperTwistCoachSchedulePolicySummary DeriveCoachSchedulePolicySummary( const FHyperTwistTrainingCoachSessionQueueState& QueueState, const FHyperTwistCoachScheduleHorizonSummary& HorizonSummary, + const FHyperTwistTrainingCoachActionClosureSummary& ClosureSummary, const FHyperTwistTrainingCoachArchivePolicySummary& ArchivePolicySummary, const FString& ReferenceUtc );