Tighten coach scheduling recovery and repository integrity

This commit is contained in:
axiomlogicnexus 2026-05-05 01:17:12 +02:00
parent 552413e438
commit 88f8a8d7f8
6 changed files with 2075 additions and 66 deletions

View file

@ -3043,6 +3043,70 @@ FHyperTwistCoachBrief UHyperTwistTrainingCoachLibrary::DeriveCoachBrief(
const FHyperTwistCoachSignal& PrimarySignal = Brief.Signals[0];
HyperTwistTrainingCoachLibraryInternal::ApplyPrimarySignalToBrief(Brief, PrimarySignal);
const bool bScheduleFacingBrief =
CoachMemorySnapshot.bNeedsScheduleFrictionRecovery
&& (PrimarySignal.SignalId == TEXT("schedule_friction")
|| Brief.FocusLane == EHyperTwistCoachFocusLane::Cadence
|| Brief.FocusLane == EHyperTwistCoachFocusLane::Stability
|| Brief.bNeedsCadenceRecovery);
if (bScheduleFacingBrief)
{
const bool bPreferFollowUpRecovery =
CoachMemorySnapshot.PreferredGuidanceLane == EHyperTwistTrainingCoachGuidanceLane::FollowUp;
const bool bPreferRecommendedRecovery =
CoachMemorySnapshot.PreferredGuidanceLane == EHyperTwistTrainingCoachGuidanceLane::Recommended;
const bool bPromoteDeferredRecovery =
CoachMemorySnapshot.PreferredQueueRecoveryAction
== EHyperTwistTrainingQueueRecoveryAction::PromoteDeferred;
const bool bLaunchRecovery =
CoachMemorySnapshot.PreferredQueueRecoveryAction
== EHyperTwistTrainingQueueRecoveryAction::LaunchRecovery;
if (CoachMemorySnapshot.SchedulePressureCaseIds.Num() > 0
&& (Brief.FocusCaseIds.Num() == 0
|| PrimarySignal.SignalId == TEXT("schedule_friction")
|| Brief.FocusLane == EHyperTwistCoachFocusLane::Cadence
|| Brief.FocusLane == EHyperTwistCoachFocusLane::Stability))
{
Brief.FocusCaseIds = CoachMemorySnapshot.SchedulePressureCaseIds;
}
if (bPreferFollowUpRecovery)
{
Brief.SuggestedMode = EHyperTwistTrainingDeliveryMode::CoachReviewed;
Brief.SuggestedSelectionPolicy = EHyperTwistTrainingSelectionPolicy::Scripted;
Brief.PrimaryActionLabel = bPromoteDeferredRecovery
? TEXT("promote_deferred_follow_up_recovery")
: (bLaunchRecovery
? TEXT("launch_follow_up_recovery")
: TEXT("recover_follow_up_schedule_friction"));
}
else if (bPreferRecommendedRecovery)
{
Brief.SuggestedMode = CoachMemorySnapshot.DominantCoachSuggestedMode;
Brief.SuggestedSelectionPolicy = CoachMemorySnapshot.bHasQueueExecutionCompletionGap
? EHyperTwistTrainingSelectionPolicy::Weighted
: CoachMemorySnapshot.DominantCoachSelectionPolicy;
Brief.PrimaryActionLabel = bPromoteDeferredRecovery
? TEXT("promote_deferred_recommended_recovery")
: (bLaunchRecovery
? TEXT("launch_recommended_recovery")
: TEXT("recover_recommended_schedule_friction"));
}
else if (CoachMemorySnapshot.bHasArchiveAwareScheduleFriction)
{
Brief.PrimaryActionLabel = TEXT("stabilize_archive_aware_schedule");
}
else
{
Brief.PrimaryActionLabel = bPromoteDeferredRecovery
? TEXT("promote_deferred_schedule_recovery")
: (bLaunchRecovery
? TEXT("launch_schedule_recovery")
: TEXT("recover_schedule_friction"));
}
}
if (Brief.FocusCaseIds.Num() == 0)
{
if (CoachMemorySnapshot.bNeedsScheduleFrictionRecovery
@ -4243,6 +4307,7 @@ FHyperTwistCoachBrief UHyperTwistTrainingCoachLibrary::RefineCoachBriefWithQueue
const FString OriginalDeckId = Brief.FocusDeckId;
const FString OriginalMethodSegmentId = Brief.FocusMethodSegmentId;
const TArray<FString> OriginalCaseIds = Brief.FocusCaseIds;
const FString OriginalPrimaryActionLabel = Brief.PrimaryActionLabel;
HyperTwistTrainingCoachLibraryInternal::ApplyPrimarySignalToBrief(Brief, Brief.Signals[0]);
if (Brief.FocusDeckId.IsEmpty())
{
@ -4256,6 +4321,15 @@ FHyperTwistCoachBrief UHyperTwistTrainingCoachLibrary::RefineCoachBriefWithQueue
{
Brief.FocusCaseIds = OriginalCaseIds;
}
if (bShouldPromoteSuppressionSignal
&& Brief.PrimaryActionLabel == QueueSuppressionSignal.SignalId
&& (!OriginalPrimaryActionLabel.IsEmpty()
&& (OriginalPrimaryActionLabel.Contains(TEXT("schedule"))
|| OriginalPrimaryActionLabel.Contains(TEXT("recovery"))
|| OriginalPrimaryActionLabel.Contains(TEXT("promote_deferred")))))
{
Brief.PrimaryActionLabel = OriginalPrimaryActionLabel;
}
}
return Brief;
@ -4447,6 +4521,12 @@ FHyperTwistCoachSignal UHyperTwistTrainingCoachLibrary::DeriveLaunchBudgetHistor
}
const bool bBroadenPreferred = CoachMemorySnapshot.PreferredLaunchBudgetBias > 0;
const bool bScheduleTighteningSource =
!bBroadenPreferred
&& CoachMemorySnapshot.PreferredLaunchBudgetSourceLabel.StartsWith(TEXT("coach-memory-schedule-"));
const bool bFollowUpScheduleTightening =
bScheduleTighteningSource
&& CoachMemorySnapshot.PreferredGuidanceLane == EHyperTwistTrainingCoachGuidanceLane::FollowUp;
const bool bCompetingGuidancePressure =
CoachMemorySnapshot.RecommendedGuidanceContinueBias > 0
&& CoachMemorySnapshot.FollowUpGuidanceContinueBias > 0;
@ -4458,19 +4538,30 @@ FHyperTwistCoachSignal UHyperTwistTrainingCoachLibrary::DeriveLaunchBudgetHistor
+ (bCompetingGuidancePressure ? 4.0f : 0.0f)
+ ((CurrentBrief.FocusLane == EHyperTwistCoachFocusLane::Cadence
|| CurrentBrief.FocusLane == EHyperTwistCoachFocusLane::Stability
|| CurrentBrief.FocusLane == EHyperTwistCoachFocusLane::ReviewBacklog) ? 3.0f : 0.0f),
|| CurrentBrief.FocusLane == EHyperTwistCoachFocusLane::ReviewBacklog) ? 3.0f : 0.0f)
+ (bScheduleTighteningSource ? 4.0f : 0.0f),
0.0f,
100.0f
);
const FString SignalId = bBroadenPreferred
? TEXT("launch_budget_history_broaden")
: TEXT("launch_budget_history_tighten");
: (bFollowUpScheduleTightening
? TEXT("launch_budget_schedule_follow_up_tighten")
: (bScheduleTighteningSource
? TEXT("launch_budget_schedule_recommended_tighten")
: TEXT("launch_budget_history_tighten")));
const EHyperTwistCoachFocusLane FocusLane = bBroadenPreferred
? EHyperTwistCoachFocusLane::ReviewBacklog
: EHyperTwistCoachFocusLane::Stability;
: (bScheduleTighteningSource
? EHyperTwistCoachFocusLane::Cadence
: EHyperTwistCoachFocusLane::Stability);
const FString Headline = bBroadenPreferred
? TEXT("Launch history favors a broader next coach run")
: TEXT("Launch history favors a tighter next coach run");
: (bScheduleTighteningSource
? (CoachMemorySnapshot.bHasBlockedFollowUpScheduleFriction
? TEXT("Schedule friction favors a tighter next coach run before broader guidance resumes")
: TEXT("Schedule recovery favors a tighter next coach run before broader guidance resumes"))
: TEXT("Launch history favors a tighter next coach run"));
const FString PreferredGuidanceLaneLabel =
CoachMemorySnapshot.PreferredGuidanceLane == EHyperTwistTrainingCoachGuidanceLane::FollowUp
? TEXT("follow-up")
@ -4490,14 +4581,22 @@ FHyperTwistCoachSignal UHyperTwistTrainingCoachLibrary::DeriveLaunchBudgetHistor
: *HyperTwistTrainingCoachLibraryInternal::DescribeCoachMemorySourceLabel(
CoachMemorySnapshot.PreferredLaunchBudgetSourceLabel)
);
const EHyperTwistTrainingDeliveryMode SuggestedMode =
CurrentBrief.SuggestedMode != EHyperTwistTrainingDeliveryMode::Timer
const EHyperTwistTrainingDeliveryMode SuggestedMode = bScheduleTighteningSource
? (bFollowUpScheduleTightening
? EHyperTwistTrainingDeliveryMode::CoachReviewed
: CoachMemorySnapshot.DominantCoachSuggestedMode)
: (CurrentBrief.SuggestedMode != EHyperTwistTrainingDeliveryMode::Timer
? CurrentBrief.SuggestedMode
: CoachMemorySnapshot.DominantCoachSuggestedMode;
const EHyperTwistTrainingSelectionPolicy SuggestedSelectionPolicy =
CurrentBrief.SuggestedSelectionPolicy != EHyperTwistTrainingSelectionPolicy::Weighted
: CoachMemorySnapshot.DominantCoachSuggestedMode);
const EHyperTwistTrainingSelectionPolicy SuggestedSelectionPolicy = bScheduleTighteningSource
? (bFollowUpScheduleTightening
? EHyperTwistTrainingSelectionPolicy::Scripted
: (CoachMemorySnapshot.bHasQueueExecutionCompletionGap
? EHyperTwistTrainingSelectionPolicy::Weighted
: CoachMemorySnapshot.DominantCoachSelectionPolicy))
: (CurrentBrief.SuggestedSelectionPolicy != EHyperTwistTrainingSelectionPolicy::Weighted
? CurrentBrief.SuggestedSelectionPolicy
: CoachMemorySnapshot.DominantCoachSelectionPolicy;
: CoachMemorySnapshot.DominantCoachSelectionPolicy);
FHyperTwistCoachSignal Signal = HyperTwistTrainingCoachLibraryInternal::MakeSignal(
SignalId,
@ -4514,11 +4613,13 @@ FHyperTwistCoachSignal UHyperTwistTrainingCoachLibrary::DeriveLaunchBudgetHistor
Signal.SuggestedMethodSegmentId = !CurrentBrief.FocusMethodSegmentId.IsEmpty()
? CurrentBrief.FocusMethodSegmentId
: CoachMemorySnapshot.FocusMethodSegmentId;
Signal.SuggestedCaseIds = CurrentBrief.FocusCaseIds.Num() > 0
? CurrentBrief.FocusCaseIds
: (CoachMemorySnapshot.WeakCaseIds.Num() > 0
? CoachMemorySnapshot.WeakCaseIds
: CoachMemorySnapshot.DueCaseIds);
Signal.SuggestedCaseIds = bScheduleTighteningSource && CoachMemorySnapshot.SchedulePressureCaseIds.Num() > 0
? CoachMemorySnapshot.SchedulePressureCaseIds
: (CurrentBrief.FocusCaseIds.Num() > 0
? CurrentBrief.FocusCaseIds
: (CoachMemorySnapshot.WeakCaseIds.Num() > 0
? CoachMemorySnapshot.WeakCaseIds
: CoachMemorySnapshot.DueCaseIds));
HyperTwistTrainingCoachLibraryInternal::ApplyGuidancePreferenceToSignal(
Signal,
CoachMemorySnapshot.PreferredGuidanceLane,
@ -4580,6 +4681,7 @@ FHyperTwistCoachBrief UHyperTwistTrainingCoachLibrary::RefineCoachBriefWithLaunc
const FString OriginalDeckId = Brief.FocusDeckId;
const FString OriginalMethodSegmentId = Brief.FocusMethodSegmentId;
const TArray<FString> OriginalCaseIds = Brief.FocusCaseIds;
const FString OriginalPrimaryActionLabel = Brief.PrimaryActionLabel;
HyperTwistTrainingCoachLibraryInternal::ApplyPrimarySignalToBrief(Brief, Brief.Signals[0]);
if (Brief.FocusDeckId.IsEmpty())
{
@ -4593,6 +4695,25 @@ FHyperTwistCoachBrief UHyperTwistTrainingCoachLibrary::RefineCoachBriefWithLaunc
{
Brief.FocusCaseIds = OriginalCaseIds;
}
if (bShouldPromoteLaunchSignal)
{
if (Brief.PrimaryActionLabel == TEXT("launch_budget_schedule_follow_up_tighten"))
{
Brief.PrimaryActionLabel = TEXT("launch_follow_up_recovery");
}
else if (Brief.PrimaryActionLabel == TEXT("launch_budget_schedule_recommended_tighten"))
{
Brief.PrimaryActionLabel = TEXT("launch_recommended_recovery");
}
else if (Brief.PrimaryActionLabel == LaunchBudgetHistorySignal.SignalId
&& (!OriginalPrimaryActionLabel.IsEmpty()
&& (OriginalPrimaryActionLabel.Contains(TEXT("schedule"))
|| OriginalPrimaryActionLabel.Contains(TEXT("recovery"))
|| OriginalPrimaryActionLabel.Contains(TEXT("promote_deferred")))))
{
Brief.PrimaryActionLabel = OriginalPrimaryActionLabel;
}
}
}
return Brief;

View file

@ -2,6 +2,7 @@
#include "HyperTwistTraining/HyperTwistTrainingCatalogLibrary.h"
#include "HyperTwistTraining/HyperTwistTrainingLibrary.h"
#include "JsonObjectConverter.h"
namespace HyperTwistTrainingRepositoryLibraryInternal
{
@ -4353,6 +4354,251 @@ namespace HyperTwistTrainingRepositoryLibraryInternal
}
}
template <typename TStruct>
FString SerializeStructToJson(const TStruct& Value)
{
FString Json;
FJsonObjectConverter::UStructToJsonObjectString(TStruct::StaticStruct(), &Value, Json, 0, 0);
return Json;
}
template <typename TStruct>
bool DoSerializedStructsMatch(const TStruct& Left, const TStruct& Right)
{
return SerializeStructToJson(Left) == SerializeStructToJson(Right);
}
TArray<FHyperTwistTrainingCaseLearnerState> NormalizeLearnerStates(
const TArray<FHyperTwistTrainingCaseLearnerState>& LearnerStates
)
{
TArray<FHyperTwistTrainingCaseLearnerState> NormalizedStates;
TMap<FString, int32> LearnerStateIndexByKey;
for (const FHyperTwistTrainingCaseLearnerState& LearnerState : LearnerStates)
{
if (!LearnerState.IsStructurallyValid())
{
continue;
}
const FString LearnerStateKey = MakeCaseKey(
LearnerState.UserId,
LearnerState.DeckId,
LearnerState.CaseId
);
if (const int32* ExistingIndex = LearnerStateIndexByKey.Find(LearnerStateKey))
{
NormalizedStates[*ExistingIndex] = LearnerState;
}
else
{
LearnerStateIndexByKey.Add(LearnerStateKey, NormalizedStates.Add(LearnerState));
}
}
NormalizedStates.Sort([](
const FHyperTwistTrainingCaseLearnerState& Left,
const FHyperTwistTrainingCaseLearnerState& Right
)
{
if (Left.UserId != Right.UserId)
{
return Left.UserId < Right.UserId;
}
if (Left.DeckId != Right.DeckId)
{
return Left.DeckId < Right.DeckId;
}
return Left.CaseId < Right.CaseId;
});
return NormalizedStates;
}
bool IsTerminalReviewPlanEntryState(const EHyperTwistTrainingReviewPlanEntryState EntryState)
{
return EntryState == EHyperTwistTrainingReviewPlanEntryState::Completed
|| EntryState == EHyperTwistTrainingReviewPlanEntryState::Skipped;
}
FHyperTwistTrainingReviewPlanEntry NormalizeReviewPlanEntry(
const FHyperTwistTrainingReviewPlanEntry& Entry
)
{
FHyperTwistTrainingReviewPlanEntry NormalizedEntry = Entry;
NormalizedEntry.AttemptCount = FMath::Max(0, NormalizedEntry.AttemptCount);
NormalizedEntry.SuccessCount = FMath::Clamp(
NormalizedEntry.SuccessCount,
0,
NormalizedEntry.AttemptCount
);
if (NormalizedEntry.PromptLabel.IsEmpty())
{
NormalizedEntry.PromptLabel = NormalizedEntry.CaseId;
}
return NormalizedEntry;
}
FHyperTwistTrainingReviewPlanState NormalizeReviewPlanState(
const FHyperTwistTrainingReviewPlanState& ReviewPlan
)
{
FHyperTwistTrainingReviewPlanState NormalizedPlan = ReviewPlan;
const FString ReferenceSeed = !ReviewPlan.ReferenceUtc.IsEmpty()
? ReviewPlan.ReferenceUtc
: (!ReviewPlan.CreatedAtUtc.IsEmpty()
? ReviewPlan.CreatedAtUtc
: (!ReviewPlan.StartedAtUtc.IsEmpty() ? ReviewPlan.StartedAtUtc : ReviewPlan.CompletedAtUtc));
NormalizedPlan.ReferenceUtc = ResolveReferenceUtc(ReferenceSeed);
if (NormalizedPlan.CreatedAtUtc.IsEmpty())
{
NormalizedPlan.CreatedAtUtc = NormalizedPlan.ReferenceUtc;
}
if (NormalizedPlan.StartedAtUtc.IsEmpty())
{
NormalizedPlan.StartedAtUtc = NormalizedPlan.CreatedAtUtc;
}
NormalizedPlan.Policy.MaxCases = FMath::Max(0, NormalizedPlan.Policy.MaxCases);
TMap<FString, int32> EntryIndexByCaseId;
TArray<FHyperTwistTrainingReviewPlanEntry> NormalizedEntries;
for (const FHyperTwistTrainingReviewPlanEntry& Entry : ReviewPlan.Entries)
{
const FHyperTwistTrainingReviewPlanEntry NormalizedEntry = NormalizeReviewPlanEntry(Entry);
if (!NormalizedEntry.IsStructurallyValid())
{
continue;
}
if (const int32* ExistingIndex = EntryIndexByCaseId.Find(NormalizedEntry.CaseId))
{
NormalizedEntries[*ExistingIndex] = NormalizedEntry;
}
else
{
EntryIndexByCaseId.Add(NormalizedEntry.CaseId, NormalizedEntries.Add(NormalizedEntry));
}
}
NormalizedPlan.Entries = NormalizedEntries;
if (!NormalizedPlan.IsStructurallyValid())
{
NormalizedPlan.CurrentEntryIndex = INDEX_NONE;
return NormalizedPlan;
}
int32 ResolvedCurrentEntryIndex = INDEX_NONE;
if (!NormalizedPlan.bCompleted
&& NormalizedPlan.Entries.IsValidIndex(NormalizedPlan.CurrentEntryIndex)
&& !IsTerminalReviewPlanEntryState(
NormalizedPlan.Entries[NormalizedPlan.CurrentEntryIndex].EntryState))
{
ResolvedCurrentEntryIndex = NormalizedPlan.CurrentEntryIndex;
}
if (ResolvedCurrentEntryIndex == INDEX_NONE && !NormalizedPlan.bCompleted)
{
ResolvedCurrentEntryIndex = NormalizedPlan.Entries.IndexOfByPredicate(
[](const FHyperTwistTrainingReviewPlanEntry& Entry)
{
return Entry.EntryState == EHyperTwistTrainingReviewPlanEntryState::Current;
}
);
if (ResolvedCurrentEntryIndex >= 0
&& IsTerminalReviewPlanEntryState(
NormalizedPlan.Entries[ResolvedCurrentEntryIndex].EntryState))
{
ResolvedCurrentEntryIndex = INDEX_NONE;
}
}
if (ResolvedCurrentEntryIndex == INDEX_NONE && !NormalizedPlan.bCompleted)
{
ResolvedCurrentEntryIndex = NormalizedPlan.Entries.IndexOfByPredicate(
[](const FHyperTwistTrainingReviewPlanEntry& Entry)
{
return Entry.EntryState == EHyperTwistTrainingReviewPlanEntryState::NeedsRepeat
|| Entry.EntryState == EHyperTwistTrainingReviewPlanEntryState::Pending;
}
);
}
int32 TerminalCount = 0;
FString LatestCompletedAtUtc;
for (const FHyperTwistTrainingReviewPlanEntry& Entry : NormalizedPlan.Entries)
{
if (IsTerminalReviewPlanEntryState(Entry.EntryState))
{
TerminalCount += 1;
}
if (!Entry.LastCompletedAtUtc.IsEmpty()
&& (LatestCompletedAtUtc.IsEmpty()
|| CompareUtcStrings(Entry.LastCompletedAtUtc, LatestCompletedAtUtc) > 0))
{
LatestCompletedAtUtc = Entry.LastCompletedAtUtc;
}
}
if (TerminalCount >= NormalizedPlan.Entries.Num())
{
NormalizedPlan.bCompleted = true;
}
if (NormalizedPlan.bCompleted)
{
NormalizedPlan.CurrentEntryIndex = INDEX_NONE;
for (FHyperTwistTrainingReviewPlanEntry& Entry : NormalizedPlan.Entries)
{
if (Entry.EntryState == EHyperTwistTrainingReviewPlanEntryState::Current)
{
Entry.EntryState = Entry.LastOutcome == EHyperTwistTrainingAttemptResult::Aborted
? EHyperTwistTrainingReviewPlanEntryState::Skipped
: (Entry.SuccessCount > 0
? EHyperTwistTrainingReviewPlanEntryState::Completed
: (Entry.AttemptCount > 0
? EHyperTwistTrainingReviewPlanEntryState::NeedsRepeat
: EHyperTwistTrainingReviewPlanEntryState::Pending));
}
}
if (NormalizedPlan.CompletedAtUtc.IsEmpty())
{
NormalizedPlan.CompletedAtUtc = !LatestCompletedAtUtc.IsEmpty()
? LatestCompletedAtUtc
: ResolveReferenceUtc(
!NormalizedPlan.StartedAtUtc.IsEmpty()
? NormalizedPlan.StartedAtUtc
: NormalizedPlan.ReferenceUtc
);
}
return NormalizedPlan;
}
NormalizedPlan.CompletedAtUtc.Reset();
NormalizedPlan.CurrentEntryIndex = ResolvedCurrentEntryIndex;
for (int32 EntryIndex = 0; EntryIndex < NormalizedPlan.Entries.Num(); ++EntryIndex)
{
FHyperTwistTrainingReviewPlanEntry& Entry = NormalizedPlan.Entries[EntryIndex];
if (IsTerminalReviewPlanEntryState(Entry.EntryState))
{
continue;
}
if (EntryIndex == ResolvedCurrentEntryIndex)
{
Entry.EntryState = EHyperTwistTrainingReviewPlanEntryState::Current;
continue;
}
if (Entry.EntryState == EHyperTwistTrainingReviewPlanEntryState::Current)
{
Entry.EntryState = Entry.LastOutcome == EHyperTwistTrainingAttemptResult::Aborted
? EHyperTwistTrainingReviewPlanEntryState::Skipped
: (Entry.AttemptCount > 0
? EHyperTwistTrainingReviewPlanEntryState::NeedsRepeat
: EHyperTwistTrainingReviewPlanEntryState::Pending);
}
}
return NormalizedPlan;
}
FHyperTwistTrainingMethodDrillFavoriteEntry NormalizeMethodDrillFavoriteEntry(
const FHyperTwistTrainingMethodDrillFavoriteEntry& FavoriteEntry
)
@ -7334,6 +7580,82 @@ FHyperTwistTrainingRepositoryIntegrityReport UHyperTwistTrainingRepositoryLibrar
SeenLearnerStateKeys.Add(LearnerStateKey);
}
if (Report.TotalRunRecordCount > 0)
{
TArray<FHyperTwistTrainingRunRecord> ValidRunRecords;
TMap<FString, int32> RunRecordIndexBySessionId;
for (const FHyperTwistTrainingRunRecord& RunRecord : RepositoryState.RunRecords)
{
if (!RunRecord.IsStructurallyValid())
{
continue;
}
const FString& SessionId = RunRecord.Session.TrainingSessionId;
if (const int32* ExistingIndex = RunRecordIndexBySessionId.Find(SessionId))
{
ValidRunRecords[*ExistingIndex] = RunRecord;
}
else
{
RunRecordIndexBySessionId.Add(SessionId, ValidRunRecords.Add(RunRecord));
}
}
ValidRunRecords = HyperTwistTrainingRepositoryLibraryInternal::SortRunRecordsByUpdateUtc(
ValidRunRecords
);
const TArray<FHyperTwistTrainingCaseLearnerState> ExpectedLearnerStates =
HyperTwistTrainingRepositoryLibraryInternal::DeriveLearnerStatesFromRunRecords(ValidRunRecords);
const TArray<FHyperTwistTrainingCaseLearnerState> ActualLearnerStates =
HyperTwistTrainingRepositoryLibraryInternal::NormalizeLearnerStates(RepositoryState.LearnerStates);
TMap<FString, FHyperTwistTrainingCaseLearnerState> ExpectedLearnerStateByKey;
for (const FHyperTwistTrainingCaseLearnerState& LearnerState : ExpectedLearnerStates)
{
ExpectedLearnerStateByKey.Add(
HyperTwistTrainingRepositoryLibraryInternal::MakeCaseKey(
LearnerState.UserId,
LearnerState.DeckId,
LearnerState.CaseId
),
LearnerState
);
}
TMap<FString, FHyperTwistTrainingCaseLearnerState> ActualLearnerStateByKey;
for (const FHyperTwistTrainingCaseLearnerState& LearnerState : ActualLearnerStates)
{
ActualLearnerStateByKey.Add(
HyperTwistTrainingRepositoryLibraryInternal::MakeCaseKey(
LearnerState.UserId,
LearnerState.DeckId,
LearnerState.CaseId
),
LearnerState
);
}
for (const TPair<FString, FHyperTwistTrainingCaseLearnerState>& Pair : ExpectedLearnerStateByKey)
{
const FHyperTwistTrainingCaseLearnerState* ActualLearnerState = ActualLearnerStateByKey.Find(Pair.Key);
if (ActualLearnerState == nullptr
|| !HyperTwistTrainingRepositoryLibraryInternal::DoSerializedStructsMatch(
*ActualLearnerState,
Pair.Value))
{
Report.OutOfSyncLearnerStateCount += 1;
}
}
for (const TPair<FString, FHyperTwistTrainingCaseLearnerState>& Pair : ActualLearnerStateByKey)
{
if (!ExpectedLearnerStateByKey.Contains(Pair.Key))
{
Report.OutOfSyncLearnerStateCount += 1;
}
}
}
Report.TotalReviewPlanCount = RepositoryState.ReviewPlans.Num();
TSet<FString> SeenReviewPlanIds;
for (const FHyperTwistTrainingReviewPlanState& ReviewPlan : RepositoryState.ReviewPlans)
@ -7352,6 +7674,37 @@ FHyperTwistTrainingRepositoryIntegrityReport UHyperTwistTrainingRepositoryLibrar
SeenReviewPlanIds.Add(ReviewPlan.PlanId);
}
TArray<FHyperTwistTrainingReviewPlanState> ValidReviewPlans;
TSet<FString> UniqueValidReviewPlanIds;
for (const FHyperTwistTrainingReviewPlanState& ReviewPlan : RepositoryState.ReviewPlans)
{
if (!ReviewPlan.IsStructurallyValid())
{
continue;
}
const FHyperTwistTrainingReviewPlanState NormalizedReviewPlan =
HyperTwistTrainingRepositoryLibraryInternal::NormalizeReviewPlanState(ReviewPlan);
if (!NormalizedReviewPlan.IsStructurallyValid())
{
Report.InvalidReviewPlanCount += 1;
continue;
}
if (UniqueValidReviewPlanIds.Contains(NormalizedReviewPlan.PlanId))
{
continue;
}
UniqueValidReviewPlanIds.Add(NormalizedReviewPlan.PlanId);
if (!HyperTwistTrainingRepositoryLibraryInternal::DoSerializedStructsMatch(
ReviewPlan,
NormalizedReviewPlan))
{
Report.OutOfSyncReviewPlanCount += 1;
}
ValidReviewPlans.Add(NormalizedReviewPlan);
}
Report.TotalStoredSessionTemplateCount = RepositoryState.StoredSessionTemplates.Num();
TSet<FString> SeenStoredTemplateKeys;
for (const FHyperTwistTrainingSessionTemplate& SessionTemplate : RepositoryState.StoredSessionTemplates)
@ -7484,6 +7837,12 @@ FHyperTwistTrainingRepositoryIntegrityReport UHyperTwistTrainingRepositoryLibrar
Report.DuplicateCoachSessionQueueHistoryCount += 1;
continue;
}
if (!HyperTwistTrainingRepositoryLibraryInternal::DoSerializedStructsMatch(
HistoryEntry,
NormalizedHistoryEntry))
{
Report.OutOfSyncCoachSessionQueueHistoryCount += 1;
}
SeenCoachSessionQueueHistoryIds.Add(NormalizedHistoryEntry.HistoryEntryId);
}
@ -7498,7 +7857,7 @@ FHyperTwistTrainingRepositoryIntegrityReport UHyperTwistTrainingRepositoryLibrar
}
TSet<FString> ValidReviewPlanIds;
for (const FHyperTwistTrainingReviewPlanState& ReviewPlan : RepositoryState.ReviewPlans)
for (const FHyperTwistTrainingReviewPlanState& ReviewPlan : ValidReviewPlans)
{
if (ReviewPlan.IsStructurallyValid())
{
@ -7519,6 +7878,7 @@ FHyperTwistTrainingRepositoryIntegrityReport UHyperTwistTrainingRepositoryLibrar
HyperTwistTrainingRepositoryLibraryInternal::NormalizeCoachActionPlan(ActionPlan);
if (!NormalizedPlan.IsStructurallyValid())
{
Report.InvalidCoachActionPlanCount += 1;
continue;
}
@ -7531,6 +7891,12 @@ FHyperTwistTrainingRepositoryIntegrityReport UHyperTwistTrainingRepositoryLibrar
continue;
}
if (!HyperTwistTrainingRepositoryLibraryInternal::DoSerializedStructsMatch(
ActionPlan,
NormalizedPlan))
{
Report.OutOfSyncCoachActionPlanCount += 1;
}
ValidCoachActionPlanIndexByKey.Add(ActionPlanKey, ValidCoachActionPlans.Add(NormalizedPlan));
}
@ -7575,17 +7941,33 @@ FHyperTwistTrainingRepositoryIntegrityReport UHyperTwistTrainingRepositoryLibrar
TSet<FString> UniqueCoachSessionQueueKeys;
for (const FHyperTwistTrainingCoachSessionQueueState& QueueState : RepositoryState.StoredCoachSessionQueues)
{
if (QueueState.IsStructurallyValid())
if (!QueueState.IsStructurallyValid())
{
const FString QueueKey = HyperTwistTrainingRepositoryLibraryInternal::MakeCoachSessionQueueKey(
QueueState.UserId,
QueueState.QueueId
);
if (!UniqueCoachSessionQueueKeys.Contains(QueueKey))
continue;
}
const FHyperTwistTrainingCoachSessionQueueState NormalizedQueueState =
HyperTwistTrainingRepositoryLibraryInternal::NormalizeCoachSessionQueueState(QueueState);
if (!NormalizedQueueState.IsStructurallyValid())
{
Report.InvalidCoachSessionQueueCount += 1;
continue;
}
const FString QueueKey = HyperTwistTrainingRepositoryLibraryInternal::MakeCoachSessionQueueKey(
NormalizedQueueState.UserId,
NormalizedQueueState.QueueId
);
if (!UniqueCoachSessionQueueKeys.Contains(QueueKey))
{
UniqueCoachSessionQueueKeys.Add(QueueKey);
if (!HyperTwistTrainingRepositoryLibraryInternal::DoSerializedStructsMatch(
QueueState,
NormalizedQueueState))
{
UniqueCoachSessionQueueKeys.Add(QueueKey);
ValidCoachSessionQueues.Add(QueueState);
Report.OutOfSyncCoachSessionQueueCount += 1;
}
ValidCoachSessionQueues.Add(NormalizedQueueState);
}
}
@ -7660,14 +8042,17 @@ FHyperTwistTrainingRepositoryIntegrityReport UHyperTwistTrainingRepositoryLibrar
if (Report.TotalRunRecordCount > 0
&& (Report.TotalLearnerStateCount == 0
|| Report.InvalidLearnerStateCount > 0
|| Report.DuplicateLearnerStateCount > 0))
|| Report.DuplicateLearnerStateCount > 0
|| Report.OutOfSyncLearnerStateCount > 0))
{
HyperTwistTrainingRepositoryLibraryInternal::AppendRepairAction(
Report.RepairActions,
TEXT("rebuild-learner-states-from-run-records")
);
}
else if (Report.InvalidLearnerStateCount > 0 || Report.DuplicateLearnerStateCount > 0)
else if (Report.InvalidLearnerStateCount > 0
|| Report.DuplicateLearnerStateCount > 0
|| Report.OutOfSyncLearnerStateCount > 0)
{
HyperTwistTrainingRepositoryLibraryInternal::AppendRepairAction(
Report.RepairActions,
@ -7688,6 +8073,13 @@ FHyperTwistTrainingRepositoryIntegrityReport UHyperTwistTrainingRepositoryLibrar
TEXT("deduplicate-review-plans-by-plan-id")
);
}
if (Report.OutOfSyncReviewPlanCount > 0)
{
HyperTwistTrainingRepositoryLibraryInternal::AppendRepairAction(
Report.RepairActions,
TEXT("normalize-review-plans")
);
}
if (Report.InvalidStoredSessionTemplateCount > 0)
{
HyperTwistTrainingRepositoryLibraryInternal::AppendRepairAction(
@ -7737,7 +8129,7 @@ FHyperTwistTrainingRepositoryIntegrityReport UHyperTwistTrainingRepositoryLibrar
TEXT("drop-invalid-coach-action-plans")
);
}
if (Report.DuplicateCoachActionPlanCount > 0)
if (Report.DuplicateCoachActionPlanCount > 0 || Report.OutOfSyncCoachActionPlanCount > 0)
{
HyperTwistTrainingRepositoryLibraryInternal::AppendRepairAction(
Report.RepairActions,
@ -7775,7 +8167,7 @@ FHyperTwistTrainingRepositoryIntegrityReport UHyperTwistTrainingRepositoryLibrar
TEXT("drop-invalid-coach-session-queues")
);
}
if (Report.DuplicateCoachSessionQueueCount > 0)
if (Report.DuplicateCoachSessionQueueCount > 0 || Report.OutOfSyncCoachSessionQueueCount > 0)
{
HyperTwistTrainingRepositoryLibraryInternal::AppendRepairAction(
Report.RepairActions,
@ -7804,7 +8196,8 @@ FHyperTwistTrainingRepositoryIntegrityReport UHyperTwistTrainingRepositoryLibrar
TEXT("drop-invalid-coach-session-queue-history")
);
}
if (Report.DuplicateCoachSessionQueueHistoryCount > 0)
if (Report.DuplicateCoachSessionQueueHistoryCount > 0
|| Report.OutOfSyncCoachSessionQueueHistoryCount > 0)
{
HyperTwistTrainingRepositoryLibraryInternal::AppendRepairAction(
Report.RepairActions,
@ -7858,28 +8251,9 @@ FHyperTwistTrainingRepositoryState UHyperTwistTrainingRepositoryLibrary::RepairR
}
else
{
TMap<FString, int32> LearnerStateIndexByKey;
for (const FHyperTwistTrainingCaseLearnerState& LearnerState : RepositoryState.LearnerStates)
{
if (!LearnerState.IsStructurallyValid())
{
continue;
}
const FString LearnerStateKey = HyperTwistTrainingRepositoryLibraryInternal::MakeCaseKey(
LearnerState.UserId,
LearnerState.DeckId,
LearnerState.CaseId
);
if (const int32* ExistingIndex = LearnerStateIndexByKey.Find(LearnerStateKey))
{
RepairedState.LearnerStates[*ExistingIndex] = LearnerState;
}
else
{
LearnerStateIndexByKey.Add(LearnerStateKey, RepairedState.LearnerStates.Add(LearnerState));
}
}
RepairedState.LearnerStates = HyperTwistTrainingRepositoryLibraryInternal::NormalizeLearnerStates(
RepositoryState.LearnerStates
);
}
TMap<FString, int32> ReviewPlanIndexById;
@ -7890,16 +8264,50 @@ FHyperTwistTrainingRepositoryState UHyperTwistTrainingRepositoryLibrary::RepairR
continue;
}
if (const int32* ExistingIndex = ReviewPlanIndexById.Find(ReviewPlan.PlanId))
const FHyperTwistTrainingReviewPlanState NormalizedReviewPlan =
HyperTwistTrainingRepositoryLibraryInternal::NormalizeReviewPlanState(ReviewPlan);
if (!NormalizedReviewPlan.IsStructurallyValid())
{
RepairedState.ReviewPlans[*ExistingIndex] = ReviewPlan;
continue;
}
if (const int32* ExistingIndex = ReviewPlanIndexById.Find(NormalizedReviewPlan.PlanId))
{
RepairedState.ReviewPlans[*ExistingIndex] = NormalizedReviewPlan;
}
else
{
ReviewPlanIndexById.Add(ReviewPlan.PlanId, RepairedState.ReviewPlans.Add(ReviewPlan));
ReviewPlanIndexById.Add(
NormalizedReviewPlan.PlanId,
RepairedState.ReviewPlans.Add(NormalizedReviewPlan)
);
}
}
RepairedState.ReviewPlans.Sort([](
const FHyperTwistTrainingReviewPlanState& Left,
const FHyperTwistTrainingReviewPlanState& Right
)
{
if (Left.UserId != Right.UserId)
{
return Left.UserId < Right.UserId;
}
const FString LeftSortUtc = !Left.CreatedAtUtc.IsEmpty() ? Left.CreatedAtUtc : Left.ReferenceUtc;
const FString RightSortUtc = !Right.CreatedAtUtc.IsEmpty() ? Right.CreatedAtUtc : Right.ReferenceUtc;
const int32 UtcCompare = HyperTwistTrainingRepositoryLibraryInternal::CompareUtcStrings(
LeftSortUtc,
RightSortUtc
);
if (UtcCompare != 0)
{
return UtcCompare > 0;
}
return Left.PlanId < Right.PlanId;
});
TMap<FString, int32> StoredTemplateIndexByKey;
for (const FHyperTwistTrainingSessionTemplate& SessionTemplate : RepositoryState.StoredSessionTemplates)
{
@ -19057,12 +19465,24 @@ FHyperTwistTrainingCoachMemorySnapshot UHyperTwistTrainingRepositoryLibrary::Der
Snapshot.ReferenceUtc
) > 3.0;
const bool bHasGenericPreferredGuidanceSource =
Snapshot.PreferredGuidanceSourceLabel.IsEmpty()
|| Snapshot.PreferredGuidanceSourceLabel == TEXT("coach-memory-default")
|| Snapshot.PreferredGuidanceSourceLabel == TEXT("coach-memory-follow-up")
|| Snapshot.PreferredGuidanceSourceLabel.StartsWith(TEXT("coach-memory-schedule-"));
if (Snapshot.bHasBlockedFollowUpScheduleFriction
|| Snapshot.ScheduleBlockedFollowUpEventCount > 0
|| Snapshot.FollowUpReadyCoachSequenceCount > 0)
{
Snapshot.PreferredGuidanceLane = EHyperTwistTrainingCoachGuidanceLane::FollowUp;
Snapshot.PreferredGuidanceSourceLabel = TEXT("coach-memory-follow-up");
if (bHasGenericPreferredGuidanceSource)
{
Snapshot.PreferredGuidanceSourceLabel =
Snapshot.bHasBlockedFollowUpScheduleFriction
|| Snapshot.ScheduleBlockedFollowUpEventCount > 0
? TEXT("coach-memory-schedule-follow-up")
: TEXT("coach-memory-follow-up");
}
}
else if (Snapshot.PreferredGuidanceLane == EHyperTwistTrainingCoachGuidanceLane::None)
{
@ -19070,7 +19490,10 @@ FHyperTwistTrainingCoachMemorySnapshot UHyperTwistTrainingRepositoryLibrary::Der
|| Snapshot.RecurringCoachCaseIds.Num() > 0
? EHyperTwistTrainingCoachGuidanceLane::Recommended
: EHyperTwistTrainingCoachGuidanceLane::FollowUp;
Snapshot.PreferredGuidanceSourceLabel = TEXT("coach-memory-default");
if (bHasGenericPreferredGuidanceSource)
{
Snapshot.PreferredGuidanceSourceLabel = TEXT("coach-memory-default");
}
}
if (Snapshot.PreferredQueueRecoveryAction == EHyperTwistTrainingQueueRecoveryAction::None
&& Snapshot.bNeedsScheduleFrictionRecovery)
@ -19081,6 +19504,74 @@ FHyperTwistTrainingCoachMemorySnapshot UHyperTwistTrainingRepositoryLibrary::Der
: EHyperTwistTrainingQueueRecoveryAction::LaunchRecovery;
Snapshot.PreferredQueueRecoveryActionSourceLabel = TEXT("coach-memory-schedule-default");
}
if (Snapshot.bNeedsScheduleFrictionRecovery)
{
const int32 ScheduleTighteningBias = FMath::Clamp(
(Snapshot.bHasBlockedFollowUpScheduleFriction ? 2 : 0)
+ (Snapshot.ScheduleBlockedFollowUpEventCount > 0 ? 1 : 0)
+ (Snapshot.bHasOutstandingDeferredScheduleWork ? 1 : 0)
+ (Snapshot.bHasQueueExecutionCompletionGap ? 1 : 0)
+ (Snapshot.bHasRepeatManualScheduleFriction ? 1 : 0),
1,
4
);
const bool bPreferFollowUpScheduleRecovery =
Snapshot.bHasBlockedFollowUpScheduleFriction
|| Snapshot.ScheduleBlockedFollowUpEventCount > 0
|| Snapshot.PreferredGuidanceLane == EHyperTwistTrainingCoachGuidanceLane::FollowUp;
const bool bPreferRecommendedScheduleRecovery =
!bPreferFollowUpScheduleRecovery
&& (Snapshot.bHasQueueExecutionCompletionGap
|| Snapshot.bHasOutstandingDeferredScheduleWork
|| Snapshot.PreferredGuidanceLane
== EHyperTwistTrainingCoachGuidanceLane::Recommended);
const bool bHasGenericLaunchBudgetSource =
Snapshot.PreferredLaunchBudgetSourceLabel.IsEmpty()
|| Snapshot.PreferredLaunchBudgetSourceLabel.StartsWith(TEXT("coach-memory-schedule-"));
Snapshot.PreferredLaunchBudgetBias =
FMath::Min(Snapshot.PreferredLaunchBudgetBias, -ScheduleTighteningBias);
if (bHasGenericLaunchBudgetSource)
{
Snapshot.PreferredLaunchBudgetSourceLabel = bPreferFollowUpScheduleRecovery
? TEXT("coach-memory-schedule-follow-up-tightened-launch")
: TEXT("coach-memory-schedule-tightened-launch");
}
if (bPreferFollowUpScheduleRecovery)
{
Snapshot.FollowUpGuidanceContinueBias += ScheduleTighteningBias;
Snapshot.SuppressedPrimaryQueuePressureCount += ScheduleTighteningBias;
Snapshot.PreferredGuidanceLane = EHyperTwistTrainingCoachGuidanceLane::FollowUp;
if (bHasGenericPreferredGuidanceSource)
{
Snapshot.PreferredGuidanceSourceLabel =
Snapshot.bHasBlockedFollowUpScheduleFriction
|| Snapshot.ScheduleBlockedFollowUpEventCount > 0
? TEXT("coach-memory-schedule-follow-up")
: TEXT("coach-memory-follow-up");
}
}
else if (bPreferRecommendedScheduleRecovery)
{
const int32 RecommendedScheduleRecoveryBias = FMath::Max(
1,
ScheduleTighteningBias - (Snapshot.bHasArchiveAwareScheduleFriction ? 1 : 0)
);
Snapshot.RecommendedGuidanceContinueBias += RecommendedScheduleRecoveryBias;
Snapshot.SuppressedFollowUpQueuePressureCount += RecommendedScheduleRecoveryBias;
if (Snapshot.PreferredGuidanceLane == EHyperTwistTrainingCoachGuidanceLane::None
|| Snapshot.PreferredGuidanceLane == EHyperTwistTrainingCoachGuidanceLane::Recommended
|| bHasGenericPreferredGuidanceSource)
{
Snapshot.PreferredGuidanceLane = EHyperTwistTrainingCoachGuidanceLane::Recommended;
if (bHasGenericPreferredGuidanceSource)
{
Snapshot.PreferredGuidanceSourceLabel = TEXT("coach-memory-schedule-recommended");
}
}
}
}
return Snapshot;
}
@ -28828,7 +29319,7 @@ FHyperTwistTrainingReviewPlanState UHyperTwistTrainingRepositoryLibrary::BuildRe
ReviewPlan.Entries[0].EntryState = EHyperTwistTrainingReviewPlanEntryState::Current;
}
return ReviewPlan;
return HyperTwistTrainingRepositoryLibraryInternal::NormalizeReviewPlanState(ReviewPlan);
}
FHyperTwistTrainingRepositoryState UHyperTwistTrainingRepositoryLibrary::UpsertReviewPlan(
@ -28837,25 +29328,27 @@ FHyperTwistTrainingRepositoryState UHyperTwistTrainingRepositoryLibrary::UpsertR
)
{
FHyperTwistTrainingRepositoryState UpdatedState = RepositoryState;
if (!ReviewPlan.IsStructurallyValid())
const FHyperTwistTrainingReviewPlanState NormalizedReviewPlan =
HyperTwistTrainingRepositoryLibraryInternal::NormalizeReviewPlanState(ReviewPlan);
if (!NormalizedReviewPlan.IsStructurallyValid())
{
return UpdatedState;
}
const int32 ExistingIndex = UpdatedState.ReviewPlans.IndexOfByPredicate(
[&ReviewPlan](const FHyperTwistTrainingReviewPlanState& ExistingPlan)
[&NormalizedReviewPlan](const FHyperTwistTrainingReviewPlanState& ExistingPlan)
{
return ExistingPlan.PlanId == ReviewPlan.PlanId;
return ExistingPlan.PlanId == NormalizedReviewPlan.PlanId;
}
);
if (ExistingIndex >= 0)
{
UpdatedState.ReviewPlans[ExistingIndex] = ReviewPlan;
UpdatedState.ReviewPlans[ExistingIndex] = NormalizedReviewPlan;
}
else
{
UpdatedState.ReviewPlans.Add(ReviewPlan);
UpdatedState.ReviewPlans.Add(NormalizedReviewPlan);
}
return UpdatedState;
@ -28885,7 +29378,7 @@ bool UHyperTwistTrainingRepositoryLibrary::TryGetReviewPlan(
return false;
}
OutReviewPlan = *ExistingPlan;
OutReviewPlan = HyperTwistTrainingRepositoryLibraryInternal::NormalizeReviewPlanState(*ExistingPlan);
return OutReviewPlan.IsStructurallyValid();
}
@ -28954,7 +29447,7 @@ FHyperTwistTrainingReviewPlanState UHyperTwistTrainingRepositoryLibrary::UpdateR
UpdatedPlan.CompletedAtUtc = !StepResult.UpdatedRunState.Session.EndedAtUtc.IsEmpty()
? StepResult.UpdatedRunState.Session.EndedAtUtc
: Attempt.CompletedAtUtc;
return UpdatedPlan;
return HyperTwistTrainingRepositoryLibraryInternal::NormalizeReviewPlanState(UpdatedPlan);
}
FString NextCaseId;
@ -28997,7 +29490,7 @@ FHyperTwistTrainingReviewPlanState UHyperTwistTrainingRepositoryLibrary::UpdateR
UpdatedPlan.Entries[NextEntryIndex].EntryState = EHyperTwistTrainingReviewPlanEntryState::Current;
}
return UpdatedPlan;
return HyperTwistTrainingRepositoryLibraryInternal::NormalizeReviewPlanState(UpdatedPlan);
}
FHyperTwistTrainingReviewFlowStatus UHyperTwistTrainingRepositoryLibrary::DeriveReviewFlowStatus(

View file

@ -205,6 +205,22 @@ namespace HyperTwistTrainingSubsystemInternal
: TEXT("successful deferred tightened-launch history is narrowing the next launch to continue deferred coach work"))
);
}
else if (CoachMemorySnapshot.PreferredLaunchBudgetSourceLabel.StartsWith(TEXT("coach-memory-schedule-")))
{
BudgetReasons.Add(
CoachMemorySnapshot.bHasBlockedFollowUpScheduleFriction
? (DeckLane == EHyperTwistTrainingCoachGuidanceLane::FollowUp
? TEXT("blocked follow-up schedule friction is keeping the continuation slice narrow enough to unblock follow-up queue work")
: (bQueuedLaunch
? TEXT("blocked follow-up schedule friction is keeping queued continuation work narrow enough to reduce deferred queue pressure")
: TEXT("blocked follow-up schedule friction is narrowing the next launch before broader guidance resumes")))
: (DeckLane == EHyperTwistTrainingCoachGuidanceLane::FollowUp
? TEXT("schedule-friction recovery is keeping the continuation slice narrow enough to stabilize follow-up queue work")
: (bQueuedLaunch
? TEXT("schedule-friction recovery is keeping queued continuation work narrow enough to reduce deferred queue pressure")
: TEXT("schedule-friction recovery is narrowing the next launch before broader guidance resumes")))
);
}
else
{
BudgetReasons.Add(TEXT("successful tightened-launch history is narrowing the next launch"));

View file

@ -6456,6 +6456,9 @@ struct FHyperTwistTrainingRepositoryIntegrityReport
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
int32 DuplicateLearnerStateCount = 0;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
int32 OutOfSyncLearnerStateCount = 0;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
int32 TotalReviewPlanCount = 0;
@ -6465,6 +6468,9 @@ struct FHyperTwistTrainingRepositoryIntegrityReport
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
int32 DuplicateReviewPlanCount = 0;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
int32 OutOfSyncReviewPlanCount = 0;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
int32 TotalStoredSessionTemplateCount = 0;
@ -6519,6 +6525,9 @@ struct FHyperTwistTrainingRepositoryIntegrityReport
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
int32 BrokenCoachActionPlanDepthCount = 0;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
int32 OutOfSyncCoachActionPlanCount = 0;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
int32 TotalCoachSessionQueueCount = 0;
@ -6528,6 +6537,9 @@ struct FHyperTwistTrainingRepositoryIntegrityReport
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
int32 DuplicateCoachSessionQueueCount = 0;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
int32 OutOfSyncCoachSessionQueueCount = 0;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
int32 MissingCoachSessionQueueSourceSessionCount = 0;
@ -6546,6 +6558,9 @@ struct FHyperTwistTrainingRepositoryIntegrityReport
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
int32 DuplicateCoachSessionQueueHistoryCount = 0;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
int32 OutOfSyncCoachSessionQueueHistoryCount = 0;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist")
int32 MissingCoachSessionQueueHistorySourceSessionCount = 0;
@ -6558,8 +6573,10 @@ struct FHyperTwistTrainingRepositoryIntegrityReport
|| DuplicateRunRecordCount > 0
|| InvalidLearnerStateCount > 0
|| DuplicateLearnerStateCount > 0
|| OutOfSyncLearnerStateCount > 0
|| InvalidReviewPlanCount > 0
|| DuplicateReviewPlanCount > 0
|| OutOfSyncReviewPlanCount > 0
|| InvalidStoredSessionTemplateCount > 0
|| DuplicateStoredSessionTemplateCount > 0
|| InvalidMethodDrillFavoriteCount > 0
@ -6574,13 +6591,16 @@ struct FHyperTwistTrainingRepositoryIntegrityReport
|| OrphanedCoachActionPlanRootCount > 0
|| CyclicCoachActionPlanLineageCount > 0
|| BrokenCoachActionPlanDepthCount > 0
|| OutOfSyncCoachActionPlanCount > 0
|| InvalidCoachSessionQueueCount > 0
|| DuplicateCoachSessionQueueCount > 0
|| OutOfSyncCoachSessionQueueCount > 0
|| MissingCoachSessionQueueSourceSessionCount > 0
|| InvalidCoachSessionQueueActiveEntryCount > 0
|| MissingCoachSessionQueueActiveSessionCount > 0
|| InvalidCoachSessionQueueHistoryCount > 0
|| DuplicateCoachSessionQueueHistoryCount > 0
|| OutOfSyncCoachSessionQueueHistoryCount > 0
|| MissingCoachSessionQueueHistorySourceSessionCount > 0;
}
};

View file

@ -56,7 +56,7 @@ Evaluate providers second.
Integrate providers only after the roles and feature-set map exist.
Current correction call from the source-exposed audit:
Current correction call from the source-exposed audit and current execution refresh:
- preserve the roadmap direction
- update the canonical planning docs so they reflect:
@ -64,6 +64,17 @@ Current correction call from the source-exposed audit:
- manifest and custody governance gaps
- pre-Phase-5 speech governance gates
- retained benchmark-oracle and mirror-intake reservations
- current code reality in which training/coaching/catalog integration is materially ahead of recognition and hypercube runtime implementation
- the imported generated-mode gap, where request/config/selection plumbing exists but the actual clean-room executor is still absent
- 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
For the current code-reality synthesis and active phase-discipline note, also read:
- `C:\visual_studio_solutions\multi_project\GPT 5.4 HyperTwist parse\116-hypertwist-handoff.md`
- `C:\visual_studio_solutions\multi_project\GPT 5.4 HyperTwist parse\119-hypertwist-current-execution-reality-and-phase-discipline.md`
- `C:\HyperTwist\docs\arch\HYPERTWIST_PI_PRIMING_FINDINGS_2026-05-04.md`
## Higher-detail planning references

File diff suppressed because it is too large Load diff