From 89a37bce08fe68f164e88e4352ddd63ea99ddf51 Mon Sep 17 00:00:00 2001 From: axiomlogicnexus Date: Wed, 6 May 2026 22:32:44 +0200 Subject: [PATCH] Persist method drill review follow-up plans --- .../HyperTwistTrainingSubsystem.cpp | 142 +++++++++++++++--- ...W_TEMPLATE_CONTINUITY_PACKET_2026-05-06.md | 96 ++++++++++++ 2 files changed, 217 insertions(+), 21 deletions(-) create mode 100644 docs/arch/HYPERTWIST_PHASE4_METHOD_DRILL_REVIEW_TEMPLATE_CONTINUITY_PACKET_2026-05-06.md diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingSubsystem.cpp b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingSubsystem.cpp index e9d1b58..524621d 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingSubsystem.cpp +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingSubsystem.cpp @@ -1348,6 +1348,19 @@ namespace HyperTwistTrainingSubsystemInternal return ReviewDeck.Cases.Num() > 0 ? ReviewDeck : FHyperTwistTrainingDeck(); } + FHyperTwistTrainingReviewPolicy BuildTemplateReviewPolicy( + const FHyperTwistTrainingSessionTemplate& SessionTemplate, + int32 CaseCount + ); + + TArray BuildTemplateReviewRecommendations( + const FHyperTwistTrainingRepositoryState& RepositoryState, + const FHyperTwistTrainingDeck& SourceDeck, + const FHyperTwistTrainingDeck& ReviewDeck, + const FString& UserId, + const FHyperTwistTrainingSessionTemplate& SessionTemplate + ); + bool ShouldResumeStoredReviewPlanFromTemplate( const FHyperTwistTrainingSessionTemplate& SessionTemplate, const FHyperTwistTrainingReviewPlanState& ReviewPlan, @@ -1364,8 +1377,8 @@ namespace HyperTwistTrainingSubsystemInternal { return false; } - if (!SessionTemplate.SourceReviewPlanId.IsEmpty() - && SessionTemplate.SourceReviewPlanId != ReviewPlan.PlanId) + if (SessionTemplate.SourceReviewPlanId.IsEmpty() + || SessionTemplate.SourceReviewPlanId != ReviewPlan.PlanId) { return false; } @@ -1378,6 +1391,42 @@ namespace HyperTwistTrainingSubsystemInternal return true; } + bool TryPrepareReviewPlanBackedTemplateLaunch( + const FHyperTwistTrainingRepositoryState& RepositoryState, + const FHyperTwistTrainingSessionTemplate& SessionTemplate, + const FHyperTwistTrainingDeck& ReviewDeck, + const FString& UserId, + FHyperTwistTrainingDeck& OutSourceDeck, + FHyperTwistTrainingReviewPolicy& OutReviewPolicy, + TArray& OutRecommendations + ) + { + OutSourceDeck = FHyperTwistTrainingDeck(); + OutReviewPolicy = FHyperTwistTrainingReviewPolicy(); + OutRecommendations.Reset(); + if (!SessionTemplate.bPreferReviewRun || !ReviewDeck.IsStructurallyValid() || UserId.IsEmpty()) + { + return false; + } + if (!UHyperTwistTrainingCatalogLibrary::TryFindDeckInCatalog( + UHyperTwistTrainingCatalogLibrary::MakePhase3TrainingCatalog(), + SessionTemplate.FocusDeckId, + OutSourceDeck)) + { + return false; + } + + OutReviewPolicy = BuildTemplateReviewPolicy(SessionTemplate, ReviewDeck.Cases.Num()); + OutRecommendations = BuildTemplateReviewRecommendations( + RepositoryState, + OutSourceDeck, + ReviewDeck, + UserId, + SessionTemplate + ); + return OutSourceDeck.IsStructurallyValid(); + } + FHyperTwistTrainingReviewPolicy BuildTemplateReviewPolicy( const FHyperTwistTrainingSessionTemplate& SessionTemplate, int32 CaseCount @@ -3577,25 +3626,16 @@ FHyperTwistTrainingRunState UHyperTwistTrainingSubsystem::StartTrainingRunFromTe FHyperTwistTrainingDeck SourceDeck; FHyperTwistTrainingReviewPolicy ReviewPolicy; TArray TemplateRecommendations; - if (MatchingTemplate->bPreferReviewRun - && UHyperTwistTrainingCatalogLibrary::TryFindDeckInCatalog( - UHyperTwistTrainingCatalogLibrary::MakePhase3TrainingCatalog(), - MatchingTemplate->FocusDeckId, - SourceDeck)) - { - ReviewPolicy = HyperTwistTrainingSubsystemInternal::BuildTemplateReviewPolicy( + const bool bPreparedReviewTemplateLaunch = + HyperTwistTrainingSubsystemInternal::TryPrepareReviewPlanBackedTemplateLaunch( + TrainingRepositoryState, *MatchingTemplate, - MaterializedDeck.Cases.Num() + MaterializedDeck, + ResolvedUserId, + SourceDeck, + ReviewPolicy, + TemplateRecommendations ); - TemplateRecommendations = - HyperTwistTrainingSubsystemInternal::BuildTemplateReviewRecommendations( - TrainingRepositoryState, - SourceDeck, - MaterializedDeck, - ResolvedUserId, - *MatchingTemplate - ); - } FHyperTwistTrainingRunState RunState = StartTrainingRunFromDeck( MaterializedDeck, @@ -3605,7 +3645,7 @@ FHyperTwistTrainingRunState UHyperTwistTrainingSubsystem::StartTrainingRunFromTe ); if (!RunState.IsStructurallyValid() || !MatchingTemplate->bPreferReviewRun - || !SourceDeck.IsStructurallyValid()) + || !bPreparedReviewTemplateLaunch) { return RunState; } @@ -3667,12 +3707,72 @@ FHyperTwistTrainingRunState UHyperTwistTrainingSubsystem::StartActiveMethodDrill *ResolvedSessionId, *MethodDrillPacketStructureToken) : ResolvedSessionId; - return StartTrainingRunFromDeck( + + if (HyperTwistTrainingSubsystemInternal::ShouldResumeStoredReviewPlanFromTemplate( + ActiveMethodDrillFollowUpTemplate, + ActiveReviewPlanState, + ActiveMethodDrillRunState.UserId)) + { + return StartRecommendedReviewRun( + StructuredSessionId, + FollowUpDeck.Cases.Num(), + ActiveMethodDrillFollowUpTemplate.SuggestedMode + ); + } + + FHyperTwistTrainingDeck SourceDeck; + FHyperTwistTrainingReviewPolicy ReviewPolicy; + TArray TemplateRecommendations; + const bool bPreparedReviewTemplateLaunch = + HyperTwistTrainingSubsystemInternal::TryPrepareReviewPlanBackedTemplateLaunch( + TrainingRepositoryState, + ActiveMethodDrillFollowUpTemplate, + FollowUpDeck, + ActiveMethodDrillRunState.UserId, + SourceDeck, + ReviewPolicy, + TemplateRecommendations + ); + FHyperTwistTrainingRunState RunState = StartTrainingRunFromDeck( FollowUpDeck, ActiveMethodDrillRunState.UserId, StructuredSessionId, ActiveMethodDrillFollowUpTemplate.SuggestedMode ); + if (!RunState.IsStructurallyValid() + || !ActiveMethodDrillFollowUpTemplate.bPreferReviewRun + || !bPreparedReviewTemplateLaunch) + { + return RunState; + } + + ActiveReviewPlanState = UHyperTwistTrainingRepositoryLibrary::BuildReviewPlan( + SourceDeck, + FollowUpDeck, + ActiveMethodDrillRunState.UserId, + TemplateRecommendations, + ReviewPolicy, + FString::Printf(TEXT("review_plan_%s"), *RunState.Session.TrainingSessionId), + RunState.Session.StartedAtUtc, + ActiveMethodDrillFollowUpTemplate.SuggestedMode + ); + if (ActiveReviewPlanState.IsStructurallyValid()) + { + TrainingRepositoryState = UHyperTwistTrainingRepositoryLibrary::UpsertReviewPlan( + TrainingRepositoryState, + ActiveReviewPlanState + ); + ActiveReviewFlowStatus = UHyperTwistTrainingRepositoryLibrary::DeriveReviewFlowStatus( + ActiveReviewPlanState + ); + RefreshRepositoryViews(); + } + else + { + ActiveReviewFlowStatus = FHyperTwistTrainingReviewFlowStatus(); + } + + return RunState; } FHyperTwistTrainingRunState UHyperTwistTrainingSubsystem::StartRecommendedReviewRun( diff --git a/docs/arch/HYPERTWIST_PHASE4_METHOD_DRILL_REVIEW_TEMPLATE_CONTINUITY_PACKET_2026-05-06.md b/docs/arch/HYPERTWIST_PHASE4_METHOD_DRILL_REVIEW_TEMPLATE_CONTINUITY_PACKET_2026-05-06.md new file mode 100644 index 0000000..fd4f7ac --- /dev/null +++ b/docs/arch/HYPERTWIST_PHASE4_METHOD_DRILL_REVIEW_TEMPLATE_CONTINUITY_PACKET_2026-05-06.md @@ -0,0 +1,96 @@ +# HyperTwist Phase 4 method-drill review template continuity packet + +Created on `2026-05-06` + +Status: + +- first-party HyperTwist packet +- bounded Phase `4` method-drill follow-up review slice + +## Purpose + +This packet closes the remaining review-template continuity gap on the method-drill follow-up launch path. + +The open tasks are: + +- stop treating method-drill follow-up templates that prefer review execution as plain deck runs +- tighten stored review-plan resumption so only explicit pending-plan templates resume an already-open plan + +It is not: + +- a new drill diagnostic packet +- a broader dashboard launch-surface packet +- a rewrite of review-template construction +- a queue / coach orchestration packet + +## Scope + +Bounded lane: + +- keep the existing method-drill follow-up template builder unchanged +- reuse the same review-template launch preparation logic now used by generic session templates +- make `StartActiveMethodDrillFollowUpRun()` persist review-plan state when its follow-up template prefers review execution +- require `SourceReviewPlanId` on template-backed stored-plan resume so generic review-preferring templates only resume when they explicitly target the open plan + +Out of scope: + +- widening into unrelated method-drill diagnostic heuristics +- changing review-plan scoring rules +- adding new template browsing or launch buttons +- changing follow-up packet structure selection + +## Why this was the right next packet + +Before this slice: + +- generic session templates that preferred review execution already launched through review-plan semantics +- method-drill follow-up templates already set `bPreferReviewRun` when their diagnostic packet asked for a review-plan bridge + +But two seams still remained: + +- `StartActiveMethodDrillFollowUpRun()` still launched those follow-up templates through `StartTrainingRunFromDeck()` only +- and the new stored-plan resume helper was broad enough to resume any open plan on the same deck, even when the template did not explicitly target that plan + +That meant: + +- generic template review continuity had landed +- while method-drill follow-up review continuity still lagged behind it +- and the stored-plan resume path still needed a tighter contract to match the last packet’s intended behavior + +So the next honest move was: + +- narrow stored-plan resume down to explicit `SourceReviewPlanId` matches +- then extend review-plan persistence into method-drill follow-up launches using that safer rule + +## What landed + +Primary code changes: + +- `UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingSubsystem.cpp` + - tightened template-backed stored-plan resume matching so only explicit pending-plan templates resume an already-open plan + - added shared review-template launch preparation for source-deck lookup, review policy, and template-backed recommendations + - `StartActiveMethodDrillFollowUpRun()` now persists a review plan after launch when the active follow-up template prefers review execution + +## Product effect + +The review-template lane is now more coherent: + +- `Resume Review Plan` keeps its direct stored-plan resumption path +- other review-preferring templates no longer accidentally reuse an unrelated open plan just because they share the same focus deck +- method-drill follow-up launches now keep repository-backed review state aligned with the review-preferring template that launched them + +## Acceptance criteria + +- template-backed stored-plan resume only occurs when the template explicitly names the open review plan +- method-drill follow-up templates with `bPreferReviewRun` persist a review plan after launch +- method-drill follow-up templates without review preference keep their existing plain-run behavior +- full product build succeeds + +## Validation checklist + +1. build `UnrealHyperTwist.sln` / `UnrealHyperTwistEditor` +2. confirm generic review-preferring templates without `SourceReviewPlanId` no longer resume arbitrary open plans +3. confirm `StartActiveMethodDrillFollowUpRun()` persists review-plan state when the follow-up template prefers review execution +4. confirm non-review method-drill follow-up launches still behave as ordinary training runs + +That is the packet.