Feed coach handoff choices into memory

This commit is contained in:
axiomlogicnexus 2026-04-27 21:58:57 +02:00
parent 33f0dad486
commit a6288a882a
12 changed files with 202 additions and 73 deletions

View file

@ -1044,7 +1044,8 @@ FHyperTwistTrainingCoachMemorySnapshot UHyperTwistContractLibrary::MakeSampleTra
QueueState,
TEXT("primary-brief"),
TEXT("coach_training_session_queued_01"),
TEXT("2026-04-28T12:22:00Z")
TEXT("2026-04-28T12:22:00Z"),
FString()
);
RepositoryState = UHyperTwistTrainingRepositoryLibrary::UpsertCoachSessionQueueState(
RepositoryState,
@ -1594,7 +1595,8 @@ FHyperTwistTrainingCoachSessionQueueExecutionSummary UHyperTwistContractLibrary:
QueueState,
TEXT("primary-brief"),
TEXT("coach_training_session_queued_01"),
TEXT("2026-04-28T12:20:00Z")
TEXT("2026-04-28T12:20:00Z"),
FString()
);
RepositoryState = UHyperTwistTrainingRepositoryLibrary::UpsertCoachSessionQueueState(
RepositoryState,

View file

@ -262,15 +262,15 @@ FHyperTwistTrainingRunState UHyperTwistCoachDashboardWidget::ExecutePrimaryCoach
FHyperTwistTrainingRunState RunState;
if (CachedCoachPanelState.bCanStartQueuedRun)
{
RunState = StartNextQueuedCoachRun(DefaultCoachMaxCases);
RunState = StartNextQueuedCoachRun(DefaultCoachMaxCases, FString());
}
else if (CachedCoachPanelState.bCanStartCoachFollowUpRun && CachedCoachPanelState.bHasFollowUpGuidance)
{
RunState = StartCoachFollowUpRun(DefaultCoachMaxCases);
RunState = StartCoachFollowUpRun(DefaultCoachMaxCases, FString());
}
else if (CachedCoachPanelState.bCanStartCoachRecommendedRun)
{
RunState = StartCoachRecommendedRun(DefaultCoachMaxCases);
RunState = StartCoachRecommendedRun(DefaultCoachMaxCases, FString());
}
SyncRetainedRunRecap();
@ -413,7 +413,7 @@ bool UHyperTwistCoachDashboardWidget::ApplyQueueRecoveryPosture()
&& CachedCoachPanelState.NextQueueSourceLabel == TEXT("primary-brief"));
if (bQueueMatchesPreference)
{
const bool bSucceeded = StartRunAndRefresh(StartNextQueuedCoachRun(DefaultCoachMaxCases));
const bool bSucceeded = StartRunAndRefresh(StartNextQueuedCoachRun(DefaultCoachMaxCases, FString()));
return RecordAndFinalizeOutcome(
TEXT("launch-queued-recovery"),
FHyperTwistCoachDashboardQueueRecoveryOutcome::EActionKind::LaunchRecovery,
@ -426,7 +426,7 @@ bool UHyperTwistCoachDashboardWidget::ApplyQueueRecoveryPosture()
&& CachedCoachPanelState.bCanStartCoachFollowUpRun
&& CachedCoachPanelState.bHasFollowUpGuidance)
{
const bool bSucceeded = StartRunAndRefresh(StartCoachFollowUpRun(DefaultCoachMaxCases));
const bool bSucceeded = StartRunAndRefresh(StartCoachFollowUpRun(DefaultCoachMaxCases, FString()));
return RecordAndFinalizeOutcome(
TEXT("launch-follow-up-recovery"),
FHyperTwistCoachDashboardQueueRecoveryOutcome::EActionKind::LaunchRecovery,
@ -435,7 +435,7 @@ bool UHyperTwistCoachDashboardWidget::ApplyQueueRecoveryPosture()
}
if (bPrefersRecommended && CachedCoachPanelState.bCanStartCoachRecommendedRun)
{
const bool bSucceeded = StartRunAndRefresh(StartCoachRecommendedRun(DefaultCoachMaxCases));
const bool bSucceeded = StartRunAndRefresh(StartCoachRecommendedRun(DefaultCoachMaxCases, FString()));
return RecordAndFinalizeOutcome(
TEXT("launch-recommended-recovery"),
FHyperTwistCoachDashboardQueueRecoveryOutcome::EActionKind::LaunchRecovery,
@ -444,7 +444,7 @@ bool UHyperTwistCoachDashboardWidget::ApplyQueueRecoveryPosture()
}
if (CachedCoachPanelState.bCanStartQueuedRun)
{
const bool bSucceeded = StartRunAndRefresh(StartNextQueuedCoachRun(DefaultCoachMaxCases));
const bool bSucceeded = StartRunAndRefresh(StartNextQueuedCoachRun(DefaultCoachMaxCases, FString()));
return RecordAndFinalizeOutcome(
TEXT("launch-queued-fallback"),
FHyperTwistCoachDashboardQueueRecoveryOutcome::EActionKind::LaunchRecovery,
@ -453,7 +453,7 @@ bool UHyperTwistCoachDashboardWidget::ApplyQueueRecoveryPosture()
}
if (CachedCoachPanelState.bCanStartCoachFollowUpRun && CachedCoachPanelState.bHasFollowUpGuidance)
{
const bool bSucceeded = StartRunAndRefresh(StartCoachFollowUpRun(DefaultCoachMaxCases));
const bool bSucceeded = StartRunAndRefresh(StartCoachFollowUpRun(DefaultCoachMaxCases, FString()));
return RecordAndFinalizeOutcome(
TEXT("launch-follow-up-fallback"),
FHyperTwistCoachDashboardQueueRecoveryOutcome::EActionKind::LaunchRecovery,
@ -462,7 +462,7 @@ bool UHyperTwistCoachDashboardWidget::ApplyQueueRecoveryPosture()
}
if (CachedCoachPanelState.bCanStartCoachRecommendedRun)
{
const bool bSucceeded = StartRunAndRefresh(StartCoachRecommendedRun(DefaultCoachMaxCases));
const bool bSucceeded = StartRunAndRefresh(StartCoachRecommendedRun(DefaultCoachMaxCases, FString()));
return RecordAndFinalizeOutcome(
TEXT("launch-recommended-fallback"),
FHyperTwistCoachDashboardQueueRecoveryOutcome::EActionKind::LaunchRecovery,
@ -972,15 +972,15 @@ bool UHyperTwistCoachDashboardWidget::ApplyGuidanceOutcomeAction()
FHyperTwistTrainingRunState RunState;
if (CachedCoachPanelState.bCanStartCoachFollowUpRun && CachedCoachPanelState.bHasFollowUpGuidance)
{
RunState = StartCoachFollowUpRun(DefaultCoachMaxCases);
RunState = StartCoachFollowUpRun(DefaultCoachMaxCases, FString());
}
else if (CachedCoachPanelState.bCanStartQueuedRun)
{
RunState = StartNextQueuedCoachRun(DefaultCoachMaxCases);
RunState = StartNextQueuedCoachRun(DefaultCoachMaxCases, FString());
}
else if (CachedCoachPanelState.bCanStartCoachRecommendedRun)
{
RunState = StartCoachRecommendedRun(DefaultCoachMaxCases);
RunState = StartCoachRecommendedRun(DefaultCoachMaxCases, FString());
}
else
{
@ -1335,7 +1335,7 @@ void UHyperTwistCoachDashboardWidget::HandleDismissRecapClicked()
void UHyperTwistCoachDashboardWidget::HandleStartQueuedRunClicked()
{
StartNextQueuedCoachRun(DefaultCoachMaxCases);
StartNextQueuedCoachRun(DefaultCoachMaxCases, TEXT("dashboard-handoff-queued"));
SyncRetainedRunRecap();
SyncSelectedQueueEntry();
SyncSelectedAttempt();
@ -1344,7 +1344,7 @@ void UHyperTwistCoachDashboardWidget::HandleStartQueuedRunClicked()
void UHyperTwistCoachDashboardWidget::HandleStartFollowUpRunClicked()
{
StartCoachFollowUpRun(DefaultCoachMaxCases);
StartCoachFollowUpRun(DefaultCoachMaxCases, TEXT("dashboard-handoff-follow-up"));
SyncRetainedRunRecap();
SyncSelectedQueueEntry();
SyncSelectedAttempt();
@ -1353,7 +1353,7 @@ void UHyperTwistCoachDashboardWidget::HandleStartFollowUpRunClicked()
void UHyperTwistCoachDashboardWidget::HandleStartRecommendedRunClicked()
{
StartCoachRecommendedRun(DefaultCoachMaxCases);
StartCoachRecommendedRun(DefaultCoachMaxCases, TEXT("dashboard-handoff-recommended"));
SyncRetainedRunRecap();
SyncSelectedQueueEntry();
SyncSelectedAttempt();

View file

@ -118,37 +118,49 @@ FHyperTwistTrainingDeck UHyperTwistTrainingPanelWidget::BuildActiveCoachFollowUp
return CachedCoachFollowUpDeck;
}
FHyperTwistTrainingRunState UHyperTwistTrainingPanelWidget::StartCoachRecommendedRun(const int32 MaxCases)
FHyperTwistTrainingRunState UHyperTwistTrainingPanelWidget::StartCoachRecommendedRun(
const int32 MaxCases,
const FString& StartActionSourceLabel
)
{
LastStepResult = FHyperTwistTrainingRunStepResult();
CachedRunState = UHyperTwistTrainingRuntimeLibrary::StartCoachRecommendedRun(
this,
DefaultSessionId + TEXT("_coach"),
MaxCases > 0 ? MaxCases : DefaultCoachMaxCases
MaxCases > 0 ? MaxCases : DefaultCoachMaxCases,
StartActionSourceLabel
);
RefreshTrainingState();
return CachedRunState;
}
FHyperTwistTrainingRunState UHyperTwistTrainingPanelWidget::StartCoachFollowUpRun(const int32 MaxCases)
FHyperTwistTrainingRunState UHyperTwistTrainingPanelWidget::StartCoachFollowUpRun(
const int32 MaxCases,
const FString& StartActionSourceLabel
)
{
LastStepResult = FHyperTwistTrainingRunStepResult();
CachedRunState = UHyperTwistTrainingRuntimeLibrary::StartCoachFollowUpRun(
this,
DefaultSessionId + TEXT("_follow_up"),
MaxCases > 0 ? MaxCases : DefaultCoachMaxCases
MaxCases > 0 ? MaxCases : DefaultCoachMaxCases,
StartActionSourceLabel
);
RefreshTrainingState();
return CachedRunState;
}
FHyperTwistTrainingRunState UHyperTwistTrainingPanelWidget::StartNextQueuedCoachRun(const int32 MaxCases)
FHyperTwistTrainingRunState UHyperTwistTrainingPanelWidget::StartNextQueuedCoachRun(
const int32 MaxCases,
const FString& StartActionSourceLabel
)
{
LastStepResult = FHyperTwistTrainingRunStepResult();
CachedRunState = UHyperTwistTrainingRuntimeLibrary::StartNextQueuedCoachRun(
this,
DefaultSessionId + TEXT("_queue"),
MaxCases > 0 ? MaxCases : DefaultCoachMaxCases
MaxCases > 0 ? MaxCases : DefaultCoachMaxCases,
StartActionSourceLabel
);
RefreshTrainingState();
return CachedRunState;

View file

@ -259,16 +259,35 @@ namespace HyperTwistTrainingRepositoryLibraryInternal
0,
5
);
const int32 ExplicitQueuedHandoffSignal = FMath::Clamp(
QueueExecutionSummary.StartedQueuedHandoffEventCount,
0,
3
);
const int32 ExplicitFollowUpHandoffSignal = FMath::Clamp(
QueueExecutionSummary.StartedFollowUpHandoffEventCount * 2,
0,
4
);
const int32 ExplicitRecommendedHandoffSignal = FMath::Clamp(
QueueExecutionSummary.StartedRecommendedHandoffEventCount * 2,
0,
4
);
const int32 WeightedFollowUpRecoverySignal =
ExplicitFollowUpRecoverySignal + ExplicitFollowUpHandoffSignal;
const int32 WeightedRecommendedRecoverySignal =
ExplicitRecommendedRecoverySignal + ExplicitRecommendedHandoffSignal;
const bool bQueueHistoryPrefersFollowUp =
DominantDeferredLane == EHyperTwistTrainingCoachGuidanceLane::FollowUp
|| (DominantCompletedLane == EHyperTwistTrainingCoachGuidanceLane::FollowUp
&& QueueExecutionSummary.PromotedEventCount > 0)
|| ExplicitFollowUpRecoverySignal > ExplicitRecommendedRecoverySignal;
|| WeightedFollowUpRecoverySignal > WeightedRecommendedRecoverySignal;
const bool bQueueHistoryPrefersRecommended =
DominantDeferredLane == EHyperTwistTrainingCoachGuidanceLane::Recommended
|| (DominantCompletedLane == EHyperTwistTrainingCoachGuidanceLane::Recommended
&& QueueExecutionSummary.PromotedEventCount > 0)
|| ExplicitRecommendedRecoverySignal > ExplicitFollowUpRecoverySignal;
|| WeightedRecommendedRecoverySignal > WeightedFollowUpRecoverySignal;
const float PromoteRecoveryActionPressure =
(QueueExecutionSummary.bHasOutstandingDeferredWork ? 4.0f : 0.0f)
+ FMath::Min(static_cast<float>(QueueExecutionSummary.DeferredEventCount) * 0.75f, 3.0f)
@ -281,7 +300,8 @@ namespace HyperTwistTrainingRepositoryLibraryInternal
+ (QueueExecutionSummary.bHasRepeatManualRescheduleFriction ? 2.0f : 0.0f)
+ (QueueExecutionSummary.bHasArchiveAwareFriction ? 1.0f : 0.0f)
+ FMath::Min(static_cast<float>(QueueExecutionSummary.CompletedEventCount), 3.0f)
+ FMath::Min(static_cast<float>(QueueExecutionSummary.ManualRescheduleEventCount) * 0.5f, 2.0f);
+ FMath::Min(static_cast<float>(QueueExecutionSummary.ManualRescheduleEventCount) * 0.5f, 2.0f)
+ static_cast<float>(ExplicitQueuedHandoffSignal);
if (PromoteRecoveryActionPressure > 0.0f || LaunchRecoveryActionPressure > 0.0f)
{
if (PromoteRecoveryActionPressure > LaunchRecoveryActionPressure)
@ -294,7 +314,9 @@ namespace HyperTwistTrainingRepositoryLibraryInternal
{
Snapshot.PreferredQueueRecoveryAction =
EHyperTwistTrainingQueueRecoveryAction::LaunchRecovery;
Snapshot.PreferredQueueRecoveryActionSourceLabel = TEXT("queue-history-launch-pressure");
Snapshot.PreferredQueueRecoveryActionSourceLabel = ExplicitQueuedHandoffSignal > 0
? TEXT("dashboard-handoff-queued")
: TEXT("queue-history-launch-pressure");
}
else
{
@ -311,14 +333,14 @@ namespace HyperTwistTrainingRepositoryLibraryInternal
{
Snapshot.FollowUpGuidanceContinueBias += FMath::Max(
1,
DeferredRecoverySignal + PromotionRecoverySignal + ExplicitFollowUpRecoverySignal
DeferredRecoverySignal + PromotionRecoverySignal + WeightedFollowUpRecoverySignal
);
}
if (bQueueHistoryPrefersRecommended)
{
Snapshot.RecommendedGuidanceContinueBias += FMath::Max(
1,
DeferredRecoverySignal + PromotionRecoverySignal + ExplicitRecommendedRecoverySignal
DeferredRecoverySignal + PromotionRecoverySignal + WeightedRecommendedRecoverySignal
- (QueueExecutionSummary.bHasBlockedFollowUpFriction ? 1 : 0)
);
}
@ -328,7 +350,7 @@ namespace HyperTwistTrainingRepositoryLibraryInternal
Snapshot.SuppressedPrimaryQueuePressureCount += FMath::Max(
1,
DeferredRecoverySignal
+ ExplicitFollowUpRecoverySignal
+ WeightedFollowUpRecoverySignal
+ (QueueExecutionSummary.bHasBlockedFollowUpFriction ? 1 : 0)
);
}
@ -338,7 +360,7 @@ namespace HyperTwistTrainingRepositoryLibraryInternal
Snapshot.SuppressedFollowUpQueuePressureCount += FMath::Max(
1,
PromotionRecoverySignal
+ ExplicitRecommendedRecoverySignal
+ WeightedRecommendedRecoverySignal
+ (QueueExecutionSummary.bHasCompletionGap ? 1 : 0)
);
}
@ -349,14 +371,18 @@ namespace HyperTwistTrainingRepositoryLibraryInternal
|| QueueExecutionSummary.BlockedFollowUpEventCount > 0))
{
Snapshot.PreferredGuidanceLane = EHyperTwistTrainingCoachGuidanceLane::FollowUp;
Snapshot.PreferredGuidanceSourceLabel = TEXT("coach-memory-queue-follow-up");
Snapshot.PreferredGuidanceSourceLabel = ExplicitFollowUpHandoffSignal > 0
? TEXT("coach-memory-dashboard-follow-up")
: TEXT("coach-memory-queue-follow-up");
}
else if (bQueueHistoryPrefersRecommended
&& (QueueExecutionSummary.PromotedEventCount > 0
|| QueueExecutionSummary.CompletedEventCount > 0))
{
Snapshot.PreferredGuidanceLane = EHyperTwistTrainingCoachGuidanceLane::Recommended;
Snapshot.PreferredGuidanceSourceLabel = TEXT("coach-memory-queue-recommended");
Snapshot.PreferredGuidanceSourceLabel = ExplicitRecommendedHandoffSignal > 0
? TEXT("coach-memory-dashboard-recommended")
: TEXT("coach-memory-queue-recommended");
}
return Snapshot;
@ -1097,6 +1123,7 @@ namespace HyperTwistTrainingRepositoryLibraryInternal
|| PreviousEntry.CompletedAtUtc != CurrentEntry.CompletedAtUtc
|| PreviousEntry.RescheduleCount != CurrentEntry.RescheduleCount
|| PreviousEntry.SourceTrainingSessionId != CurrentEntry.SourceTrainingSessionId
|| PreviousEntry.StartActionSourceLabel != CurrentEntry.StartActionSourceLabel
|| PreviousEntry.bWasManuallyRescheduled != CurrentEntry.bWasManuallyRescheduled;
}
@ -1165,13 +1192,14 @@ namespace HyperTwistTrainingRepositoryLibraryInternal
)
{
return FString::Printf(
TEXT("%s|%s|%d|%s|%s|%s"),
TEXT("%s|%s|%d|%s|%s|%s|%s"),
*Entry.QueueId,
*Entry.EntryId,
static_cast<int32>(Entry.EventKind),
*Entry.EventAtUtc,
*Entry.WorkFingerprint,
*Entry.SourceTrainingSessionId
*Entry.SourceTrainingSessionId,
*Entry.StartActionSourceLabel
);
}
@ -1212,6 +1240,7 @@ namespace HyperTwistTrainingRepositoryLibraryInternal
: CurrentEntry.EntryState;
HistoryEntry.NewEntryState = CurrentEntry.EntryState;
HistoryEntry.SourceTrainingSessionId = CurrentEntry.SourceTrainingSessionId;
HistoryEntry.StartActionSourceLabel = CurrentEntry.StartActionSourceLabel;
HistoryEntry.DeferredUntilUtc = CurrentEntry.DeferredUntilUtc;
HistoryEntry.DeferralReasonLabel = CurrentEntry.DeferralReasonLabel;
HistoryEntry.FocusDeckId = CurrentEntry.FocusDeckId;
@ -5443,16 +5472,35 @@ FHyperTwistTrainingCoachMemorySnapshot UHyperTwistTrainingRepositoryLibrary::Der
0,
5
);
const int32 ExplicitQueuedHandoffSignal = FMath::Clamp(
QueueExecutionSummary.StartedQueuedHandoffEventCount,
0,
3
);
const int32 ExplicitFollowUpHandoffSignal = FMath::Clamp(
QueueExecutionSummary.StartedFollowUpHandoffEventCount * 2,
0,
4
);
const int32 ExplicitRecommendedHandoffSignal = FMath::Clamp(
QueueExecutionSummary.StartedRecommendedHandoffEventCount * 2,
0,
4
);
const int32 WeightedFollowUpRecoverySignal =
ExplicitFollowUpRecoverySignal + ExplicitFollowUpHandoffSignal;
const int32 WeightedRecommendedRecoverySignal =
ExplicitRecommendedRecoverySignal + ExplicitRecommendedHandoffSignal;
const bool bQueueHistoryPrefersFollowUp =
DominantDeferredLane == EHyperTwistTrainingCoachGuidanceLane::FollowUp
|| (DominantCompletedLane == EHyperTwistTrainingCoachGuidanceLane::FollowUp
&& QueueExecutionSummary.PromotedEventCount > 0)
|| ExplicitFollowUpRecoverySignal > ExplicitRecommendedRecoverySignal;
|| WeightedFollowUpRecoverySignal > WeightedRecommendedRecoverySignal;
const bool bQueueHistoryPrefersRecommended =
DominantDeferredLane == EHyperTwistTrainingCoachGuidanceLane::Recommended
|| (DominantCompletedLane == EHyperTwistTrainingCoachGuidanceLane::Recommended
&& QueueExecutionSummary.PromotedEventCount > 0)
|| ExplicitRecommendedRecoverySignal > ExplicitFollowUpRecoverySignal;
|| WeightedRecommendedRecoverySignal > WeightedFollowUpRecoverySignal;
const float PromoteRecoveryActionPressure =
(QueueExecutionSummary.bHasOutstandingDeferredWork ? 4.0f : 0.0f)
+ FMath::Min(static_cast<float>(QueueExecutionSummary.DeferredEventCount) * 0.75f, 3.0f)
@ -5465,7 +5513,8 @@ FHyperTwistTrainingCoachMemorySnapshot UHyperTwistTrainingRepositoryLibrary::Der
+ (QueueExecutionSummary.bHasRepeatManualRescheduleFriction ? 2.0f : 0.0f)
+ (QueueExecutionSummary.bHasArchiveAwareFriction ? 1.0f : 0.0f)
+ FMath::Min(static_cast<float>(QueueExecutionSummary.CompletedEventCount), 3.0f)
+ FMath::Min(static_cast<float>(QueueExecutionSummary.ManualRescheduleEventCount) * 0.5f, 2.0f);
+ FMath::Min(static_cast<float>(QueueExecutionSummary.ManualRescheduleEventCount) * 0.5f, 2.0f)
+ static_cast<float>(ExplicitQueuedHandoffSignal);
if (PromoteRecoveryActionPressure > 0.0f || LaunchRecoveryActionPressure > 0.0f)
{
if (PromoteRecoveryActionPressure > LaunchRecoveryActionPressure)
@ -5478,7 +5527,9 @@ FHyperTwistTrainingCoachMemorySnapshot UHyperTwistTrainingRepositoryLibrary::Der
{
Snapshot.PreferredQueueRecoveryAction =
EHyperTwistTrainingQueueRecoveryAction::LaunchRecovery;
Snapshot.PreferredQueueRecoveryActionSourceLabel = TEXT("queue-history-launch-pressure");
Snapshot.PreferredQueueRecoveryActionSourceLabel = ExplicitQueuedHandoffSignal > 0
? TEXT("dashboard-handoff-queued")
: TEXT("queue-history-launch-pressure");
}
else
{
@ -5495,14 +5546,14 @@ FHyperTwistTrainingCoachMemorySnapshot UHyperTwistTrainingRepositoryLibrary::Der
{
Snapshot.FollowUpGuidanceContinueBias += FMath::Max(
1,
DeferredRecoverySignal + PromotionRecoverySignal + ExplicitFollowUpRecoverySignal
DeferredRecoverySignal + PromotionRecoverySignal + WeightedFollowUpRecoverySignal
);
}
if (bQueueHistoryPrefersRecommended)
{
Snapshot.RecommendedGuidanceContinueBias += FMath::Max(
1,
DeferredRecoverySignal + PromotionRecoverySignal + ExplicitRecommendedRecoverySignal
DeferredRecoverySignal + PromotionRecoverySignal + WeightedRecommendedRecoverySignal
- (QueueExecutionSummary.bHasBlockedFollowUpFriction ? 1 : 0)
);
}
@ -5512,7 +5563,7 @@ FHyperTwistTrainingCoachMemorySnapshot UHyperTwistTrainingRepositoryLibrary::Der
Snapshot.SuppressedPrimaryQueuePressureCount += FMath::Max(
1,
DeferredRecoverySignal
+ ExplicitFollowUpRecoverySignal
+ WeightedFollowUpRecoverySignal
+ (QueueExecutionSummary.bHasBlockedFollowUpFriction ? 1 : 0)
);
}
@ -5522,7 +5573,7 @@ FHyperTwistTrainingCoachMemorySnapshot UHyperTwistTrainingRepositoryLibrary::Der
Snapshot.SuppressedFollowUpQueuePressureCount += FMath::Max(
1,
PromotionRecoverySignal
+ ExplicitRecommendedRecoverySignal
+ WeightedRecommendedRecoverySignal
+ (QueueExecutionSummary.bHasCompletionGap ? 1 : 0)
);
}
@ -5617,7 +5668,9 @@ FHyperTwistTrainingCoachMemorySnapshot UHyperTwistTrainingRepositoryLibrary::Der
|| QueueExecutionSummary.BlockedFollowUpEventCount > 0))
{
Snapshot.PreferredGuidanceLane = EHyperTwistTrainingCoachGuidanceLane::FollowUp;
Snapshot.PreferredGuidanceSourceLabel = TEXT("coach-memory-queue-follow-up");
Snapshot.PreferredGuidanceSourceLabel = ExplicitFollowUpHandoffSignal > 0
? TEXT("coach-memory-dashboard-follow-up")
: TEXT("coach-memory-queue-follow-up");
}
else if (bQueueHistoryPrefersRecommended
&& Snapshot.PreferredGuidanceLane != EHyperTwistTrainingCoachGuidanceLane::FollowUp
@ -5625,7 +5678,9 @@ FHyperTwistTrainingCoachMemorySnapshot UHyperTwistTrainingRepositoryLibrary::Der
|| QueueExecutionSummary.CompletedEventCount > 0))
{
Snapshot.PreferredGuidanceLane = EHyperTwistTrainingCoachGuidanceLane::Recommended;
Snapshot.PreferredGuidanceSourceLabel = TEXT("coach-memory-queue-recommended");
Snapshot.PreferredGuidanceSourceLabel = ExplicitRecommendedHandoffSignal > 0
? TEXT("coach-memory-dashboard-recommended")
: TEXT("coach-memory-queue-recommended");
}
}
}
@ -6689,7 +6744,8 @@ FHyperTwistTrainingCoachSessionQueueState UHyperTwistTrainingRepositoryLibrary::
const FHyperTwistTrainingCoachSessionQueueState& QueueState,
const FString& SourceLabel,
const FString& TrainingSessionId,
const FString& StartedAtUtc
const FString& StartedAtUtc,
const FString& StartActionSourceLabel
)
{
FHyperTwistTrainingCoachSessionQueueState UpdatedQueueState =
@ -6724,6 +6780,7 @@ FHyperTwistTrainingCoachSessionQueueState UHyperTwistTrainingRepositoryLibrary::
{
Entry.EntryState = EHyperTwistTrainingCoachSessionQueueEntryState::Pending;
Entry.SourceTrainingSessionId.Reset();
Entry.StartActionSourceLabel.Reset();
Entry.StartedAtUtc.Reset();
Entry.bCanStartImmediately = false;
}
@ -6733,6 +6790,9 @@ FHyperTwistTrainingCoachSessionQueueState UHyperTwistTrainingRepositoryLibrary::
UpdatedQueueState.Entries[ActivatedIndex];
ActiveEntry.EntryState = EHyperTwistTrainingCoachSessionQueueEntryState::InProgress;
ActiveEntry.SourceTrainingSessionId = TrainingSessionId;
ActiveEntry.StartActionSourceLabel = !StartActionSourceLabel.IsEmpty()
? StartActionSourceLabel
: SourceLabel;
ActiveEntry.ScheduledAtUtc = !ActiveEntry.ScheduledAtUtc.IsEmpty()
? ActiveEntry.ScheduledAtUtc
: ResolvedStartedAtUtc;
@ -7223,6 +7283,18 @@ FHyperTwistTrainingCoachSessionQueueExecutionSummary UHyperTwistTrainingReposito
{
Summary.StartedRecommendedEventCount += 1;
}
if (HistoryEntry.StartActionSourceLabel == TEXT("dashboard-handoff-queued"))
{
Summary.StartedQueuedHandoffEventCount += 1;
}
else if (HistoryEntry.StartActionSourceLabel == TEXT("dashboard-handoff-follow-up"))
{
Summary.StartedFollowUpHandoffEventCount += 1;
}
else if (HistoryEntry.StartActionSourceLabel == TEXT("dashboard-handoff-recommended"))
{
Summary.StartedRecommendedHandoffEventCount += 1;
}
if (Summary.LastStartedAtUtc.IsEmpty()
|| HyperTwistTrainingRepositoryLibraryInternal::CompareUtcStrings(
HistoryEntry.EventAtUtc,

View file

@ -1004,12 +1004,13 @@ FHyperTwistTrainingRunState UHyperTwistTrainingRuntimeLibrary::StartRecommendedR
FHyperTwistTrainingRunState UHyperTwistTrainingRuntimeLibrary::StartCoachRecommendedRun(
UObject* WorldContextObject,
const FString& SessionId,
int32 MaxCases
int32 MaxCases,
const FString& StartActionSourceLabel
)
{
if (UHyperTwistTrainingSubsystem* TrainingSubsystem = GetTrainingSubsystem(WorldContextObject))
{
return TrainingSubsystem->StartCoachRecommendedRun(SessionId, MaxCases);
return TrainingSubsystem->StartCoachRecommendedRun(SessionId, MaxCases, StartActionSourceLabel);
}
return FHyperTwistTrainingRunState();
@ -1018,12 +1019,13 @@ FHyperTwistTrainingRunState UHyperTwistTrainingRuntimeLibrary::StartCoachRecomme
FHyperTwistTrainingRunState UHyperTwistTrainingRuntimeLibrary::StartCoachFollowUpRun(
UObject* WorldContextObject,
const FString& SessionId,
int32 MaxCases
int32 MaxCases,
const FString& StartActionSourceLabel
)
{
if (UHyperTwistTrainingSubsystem* TrainingSubsystem = GetTrainingSubsystem(WorldContextObject))
{
return TrainingSubsystem->StartCoachFollowUpRun(SessionId, MaxCases);
return TrainingSubsystem->StartCoachFollowUpRun(SessionId, MaxCases, StartActionSourceLabel);
}
return FHyperTwistTrainingRunState();
@ -1032,12 +1034,13 @@ FHyperTwistTrainingRunState UHyperTwistTrainingRuntimeLibrary::StartCoachFollowU
FHyperTwistTrainingRunState UHyperTwistTrainingRuntimeLibrary::StartNextQueuedCoachRun(
UObject* WorldContextObject,
const FString& SessionId,
int32 MaxCases
int32 MaxCases,
const FString& StartActionSourceLabel
)
{
if (UHyperTwistTrainingSubsystem* TrainingSubsystem = GetTrainingSubsystem(WorldContextObject))
{
return TrainingSubsystem->StartNextQueuedCoachRun(SessionId, MaxCases);
return TrainingSubsystem->StartNextQueuedCoachRun(SessionId, MaxCases, StartActionSourceLabel);
}
return FHyperTwistTrainingRunState();

View file

@ -129,7 +129,8 @@ FHyperTwistTrainingRunState AHyperTwistTrainingSessionActor::StartCoachRecommend
CachedRunState = UHyperTwistTrainingRuntimeLibrary::StartCoachRecommendedRun(
this,
AutoStartSessionId + TEXT("_coach"),
MaxCases > 0 ? MaxCases : DefaultCoachMaxCases
MaxCases > 0 ? MaxCases : DefaultCoachMaxCases,
FString()
);
RefreshCachedTrainingState();
return CachedRunState;
@ -142,7 +143,8 @@ FHyperTwistTrainingRunState AHyperTwistTrainingSessionActor::StartCoachFollowUpT
CachedRunState = UHyperTwistTrainingRuntimeLibrary::StartCoachFollowUpRun(
this,
AutoStartSessionId + TEXT("_follow_up"),
MaxCases > 0 ? MaxCases : DefaultCoachMaxCases
MaxCases > 0 ? MaxCases : DefaultCoachMaxCases,
FString()
);
RefreshCachedTrainingState();
return CachedRunState;
@ -155,7 +157,8 @@ FHyperTwistTrainingRunState AHyperTwistTrainingSessionActor::StartNextQueuedCoac
CachedRunState = UHyperTwistTrainingRuntimeLibrary::StartNextQueuedCoachRun(
this,
AutoStartSessionId + TEXT("_queue"),
MaxCases > 0 ? MaxCases : DefaultCoachMaxCases
MaxCases > 0 ? MaxCases : DefaultCoachMaxCases,
FString()
);
RefreshCachedTrainingState();
return CachedRunState;

View file

@ -1389,7 +1389,8 @@ FHyperTwistTrainingRunState UHyperTwistTrainingSubsystem::StartRecommendedReview
FHyperTwistTrainingRunState UHyperTwistTrainingSubsystem::StartCoachRecommendedRun(
const FString& SessionId,
int32 MaxCases
int32 MaxCases,
const FString& StartActionSourceLabel
)
{
const FHyperTwistTrainingDeck CoachDeck = BuildActiveCoachRecommendedDeck(MaxCases);
@ -1434,7 +1435,8 @@ FHyperTwistTrainingRunState UHyperTwistTrainingSubsystem::StartCoachRecommendedR
QueueStateBeforeStart,
TEXT("primary-brief"),
RunState.Session.TrainingSessionId,
RunState.Session.StartedAtUtc
RunState.Session.StartedAtUtc,
StartActionSourceLabel
);
if (UpdatedQueueState.IsStructurallyValid())
{
@ -1451,7 +1453,8 @@ FHyperTwistTrainingRunState UHyperTwistTrainingSubsystem::StartCoachRecommendedR
FHyperTwistTrainingRunState UHyperTwistTrainingSubsystem::StartCoachFollowUpRun(
const FString& SessionId,
int32 MaxCases
int32 MaxCases,
const FString& StartActionSourceLabel
)
{
const FHyperTwistTrainingDeck FollowUpDeck = BuildActiveCoachFollowUpDeck(MaxCases);
@ -1496,7 +1499,8 @@ FHyperTwistTrainingRunState UHyperTwistTrainingSubsystem::StartCoachFollowUpRun(
QueueStateBeforeStart,
TEXT("follow-up-brief"),
RunState.Session.TrainingSessionId,
RunState.Session.StartedAtUtc
RunState.Session.StartedAtUtc,
StartActionSourceLabel
);
if (UpdatedQueueState.IsStructurallyValid())
{
@ -1513,7 +1517,8 @@ FHyperTwistTrainingRunState UHyperTwistTrainingSubsystem::StartCoachFollowUpRun(
FHyperTwistTrainingRunState UHyperTwistTrainingSubsystem::StartNextQueuedCoachRun(
const FString& SessionId,
int32 MaxCases
int32 MaxCases,
const FString& StartActionSourceLabel
)
{
const FHyperTwistTrainingCoachSessionQueueStateEntry* NextRunnableEntry =
@ -1525,11 +1530,11 @@ FHyperTwistTrainingRunState UHyperTwistTrainingSubsystem::StartNextQueuedCoachRu
if (NextRunnableEntry->SourceLabel == TEXT("primary-brief"))
{
return StartCoachRecommendedRun(SessionId, MaxCases);
return StartCoachRecommendedRun(SessionId, MaxCases, StartActionSourceLabel);
}
if (NextRunnableEntry->SourceLabel == TEXT("follow-up-brief"))
{
return StartCoachFollowUpRun(SessionId, MaxCases);
return StartCoachFollowUpRun(SessionId, MaxCases, StartActionSourceLabel);
}
const FHyperTwistTrainingDeck QueueDeck = HyperTwistTrainingSubsystemInternal::BuildCoachQueueDeckFromEntry(
@ -1590,7 +1595,8 @@ FHyperTwistTrainingRunState UHyperTwistTrainingSubsystem::StartNextQueuedCoachRu
QueueStateBeforeStart,
NextRunnableEntry->SourceLabel,
RunState.Session.TrainingSessionId,
RunState.Session.StartedAtUtc
RunState.Session.StartedAtUtc,
StartActionSourceLabel
);
if (UpdatedQueueState.IsStructurallyValid())
{

View file

@ -110,13 +110,22 @@ public:
FHyperTwistTrainingDeck BuildActiveCoachFollowUpDeck(int32 MaxCases);
UFUNCTION(BlueprintCallable, Category = "HyperTwist|Training|Coach")
FHyperTwistTrainingRunState StartCoachRecommendedRun(int32 MaxCases);
FHyperTwistTrainingRunState StartCoachRecommendedRun(
int32 MaxCases,
const FString& StartActionSourceLabel
);
UFUNCTION(BlueprintCallable, Category = "HyperTwist|Training|Coach")
FHyperTwistTrainingRunState StartCoachFollowUpRun(int32 MaxCases);
FHyperTwistTrainingRunState StartCoachFollowUpRun(
int32 MaxCases,
const FString& StartActionSourceLabel
);
UFUNCTION(BlueprintCallable, Category = "HyperTwist|Training|Coach")
FHyperTwistTrainingRunState StartNextQueuedCoachRun(int32 MaxCases);
FHyperTwistTrainingRunState StartNextQueuedCoachRun(
int32 MaxCases,
const FString& StartActionSourceLabel
);
UFUNCTION(BlueprintCallable, Category = "HyperTwist|Training|Coach")
FHyperTwistTrainingRunState StartCoachQueueEntry(

View file

@ -367,7 +367,8 @@ public:
const FHyperTwistTrainingCoachSessionQueueState& QueueState,
const FString& SourceLabel,
const FString& TrainingSessionId,
const FString& StartedAtUtc
const FString& StartedAtUtc,
const FString& StartActionSourceLabel
);
UFUNCTION(BlueprintPure, Category = "HyperTwist|Training|Repository|Coach")

View file

@ -407,21 +407,24 @@ public:
static FHyperTwistTrainingRunState StartCoachRecommendedRun(
UObject* WorldContextObject,
const FString& SessionId,
int32 MaxCases
int32 MaxCases,
const FString& StartActionSourceLabel
);
UFUNCTION(BlueprintCallable, Category = "HyperTwist|Training|Coach", meta = (WorldContext = "WorldContextObject"))
static FHyperTwistTrainingRunState StartCoachFollowUpRun(
UObject* WorldContextObject,
const FString& SessionId,
int32 MaxCases
int32 MaxCases,
const FString& StartActionSourceLabel
);
UFUNCTION(BlueprintCallable, Category = "HyperTwist|Training|Coach", meta = (WorldContext = "WorldContextObject"))
static FHyperTwistTrainingRunState StartNextQueuedCoachRun(
UObject* WorldContextObject,
const FString& SessionId,
int32 MaxCases
int32 MaxCases,
const FString& StartActionSourceLabel
);
UFUNCTION(BlueprintCallable, Category = "HyperTwist|Training|Coach", meta = (WorldContext = "WorldContextObject"))

View file

@ -327,19 +327,22 @@ public:
UFUNCTION(BlueprintCallable, Category = "HyperTwist|Training|Coach")
FHyperTwistTrainingRunState StartCoachRecommendedRun(
const FString& SessionId,
int32 MaxCases
int32 MaxCases,
const FString& StartActionSourceLabel
);
UFUNCTION(BlueprintCallable, Category = "HyperTwist|Training|Coach")
FHyperTwistTrainingRunState StartCoachFollowUpRun(
const FString& SessionId,
int32 MaxCases
int32 MaxCases,
const FString& StartActionSourceLabel
);
UFUNCTION(BlueprintCallable, Category = "HyperTwist|Training|Coach")
FHyperTwistTrainingRunState StartNextQueuedCoachRun(
const FString& SessionId,
int32 MaxCases
int32 MaxCases,
const FString& StartActionSourceLabel
);
UFUNCTION(BlueprintCallable, Category = "HyperTwist|Training|Coach")

View file

@ -3001,6 +3001,9 @@ struct FHyperTwistTrainingCoachSessionQueueStateEntry
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString SourceTrainingSessionId;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString StartActionSourceLabel;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
int32 SlotIndex = 0;
@ -3189,6 +3192,9 @@ struct FHyperTwistTrainingCoachSessionQueueHistoryEntry
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString SourceTrainingSessionId;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString StartActionSourceLabel;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
FString DeferredUntilUtc;
@ -3273,6 +3279,15 @@ struct FHyperTwistTrainingCoachSessionQueueExecutionSummary
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
int32 StartedRecommendedEventCount = 0;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
int32 StartedQueuedHandoffEventCount = 0;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
int32 StartedFollowUpHandoffEventCount = 0;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
int32 StartedRecommendedHandoffEventCount = 0;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
int32 PromotedFollowUpEventCount = 0;