From a2d2f089bbb417449d4bff5f0a6db656fd17f06a Mon Sep 17 00:00:00 2001 From: axiomlogicnexus Date: Thu, 7 May 2026 20:29:00 +0200 Subject: [PATCH] Restore carryover review action-plan continuity --- .../HyperTwistTrainingSubsystem.cpp | 136 ++++++++++++++++++ ...RTWIST_ROADMAP_OVERHAUL_EXPANSION_GUIDE.md | 6 +- ...RESUMPTION_CONTINUITY_PACKET_2026-05-07.md | 104 ++++++++++++++ 3 files changed, 243 insertions(+), 3 deletions(-) create mode 100644 docs/arch/HYPERTWIST_PHASE4_CARRYOVER_REVIEW_ACTION_PLAN_RESUMPTION_CONTINUITY_PACKET_2026-05-07.md diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingSubsystem.cpp b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingSubsystem.cpp index 0270bc1..c4b7309 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingSubsystem.cpp +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingSubsystem.cpp @@ -1445,6 +1445,100 @@ namespace HyperTwistTrainingSubsystemInternal return true; } + FHyperTwistCoachBrief BuildCarryoverReviewResumeBrief( + const FHyperTwistTrainingReviewPlanState& ReviewPlan, + const FHyperTwistTrainingCoachActionPlan& CarryForwardActionPlan, + const FHyperTwistCoachBrief& CurrentBrief, + const FHyperTwistCoachBrief& FollowUpBrief, + const FHyperTwistTrainingDeck& ReviewDeck + ) + { + FHyperTwistCoachBrief Brief = !FollowUpBrief.UserId.IsEmpty() ? FollowUpBrief : CurrentBrief; + const TArray ReviewCaseIds = ExtractCaseIdsFromDeck(ReviewDeck); + + if (Brief.UserId.IsEmpty()) + { + Brief.UserId = !ReviewPlan.UserId.IsEmpty() ? ReviewPlan.UserId : CarryForwardActionPlan.UserId; + } + if (Brief.ReferenceUtc.IsEmpty()) + { + Brief.ReferenceUtc = !ReviewPlan.ReferenceUtc.IsEmpty() + ? ReviewPlan.ReferenceUtc + : CarryForwardActionPlan.ReferenceUtc; + } + if (Brief.TrainingSessionId.IsEmpty()) + { + Brief.TrainingSessionId = CarryForwardActionPlan.SourceSessionId; + } + if (Brief.SourceReviewPlanId.IsEmpty()) + { + Brief.SourceReviewPlanId = ReviewPlan.PlanId; + } + if (Brief.CarryForwardPlanId.IsEmpty()) + { + Brief.CarryForwardPlanId = CarryForwardActionPlan.PlanId; + } + if (Brief.RootPlanId.IsEmpty()) + { + Brief.RootPlanId = !CarryForwardActionPlan.RootPlanId.IsEmpty() + ? CarryForwardActionPlan.RootPlanId + : CarryForwardActionPlan.PlanId; + } + Brief.FollowUpDepth = FMath::Max(Brief.FollowUpDepth, CarryForwardActionPlan.FollowUpDepth + 1); + if (Brief.FocusDeckId.IsEmpty()) + { + Brief.FocusDeckId = !ReviewPlan.SourceDeckId.IsEmpty() + ? ReviewPlan.SourceDeckId + : (!CarryForwardActionPlan.FocusDeckId.IsEmpty() + ? CarryForwardActionPlan.FocusDeckId + : ReviewDeck.DeckId); + } + if (Brief.FocusMethodSegmentId.IsEmpty()) + { + Brief.FocusMethodSegmentId = CarryForwardActionPlan.FocusMethodSegmentId; + } + if (ReviewCaseIds.Num() > 0) + { + Brief.FocusCaseIds = ReviewCaseIds; + } + if (Brief.Headline.IsEmpty()) + { + Brief.Headline = CarryForwardActionPlan.Headline.IsEmpty() + ? TEXT("Resume review carryover") + : FString::Printf(TEXT("%s (carryover review)"), *CarryForwardActionPlan.Headline); + } + if (Brief.PrimaryActionLabel.IsEmpty()) + { + Brief.PrimaryActionLabel = TEXT("resume_review_plan"); + } + Brief.SuggestedMode = ReviewPlan.DeliveryMode; + Brief.SuggestedSelectionPolicy = ReviewDeck.SelectionPolicy; + if (Brief.FocusLane == EHyperTwistCoachFocusLane::Stability) + { + Brief.FocusLane = EHyperTwistCoachFocusLane::ReviewBacklog; + } + if (Brief.PriorityScore <= 0.0f) + { + Brief.PriorityScore = CarryForwardActionPlan.PriorityScore > 0.0f + ? CarryForwardActionPlan.PriorityScore + : 60.0f; + } + if (Brief.Priority == EHyperTwistCoachPriority::Informational && Brief.PriorityScore > 0.0f) + { + Brief.Priority = CoachPriorityFromScore(Brief.PriorityScore); + } + Brief.bNeedsCoachReview = Brief.bNeedsCoachReview || CarryForwardActionPlan.bNeedsCoachReview; + Brief.bNeedsCadenceRecovery = + Brief.bNeedsCadenceRecovery || CarryForwardActionPlan.bNeedsCadenceRecovery; + Brief.bNeedsVarietyRotation = + Brief.bNeedsVarietyRotation || CarryForwardActionPlan.bNeedsVarietyRotation; + Brief.bPreferRecognitionReplayReview = + Brief.bPreferRecognitionReplayReview + || CarryForwardActionPlan.bPreferRecognitionReplayReview + || ReviewPlan.DeliveryMode == EHyperTwistTrainingDeliveryMode::RecognitionAssisted; + return Brief; + } + bool TryPrepareReviewPlanBackedTemplateLaunch( const FHyperTwistTrainingRepositoryState& RepositoryState, const FHyperTwistTrainingSessionTemplate& SessionTemplate, @@ -3905,6 +3999,9 @@ FHyperTwistTrainingRunState UHyperTwistTrainingSubsystem::StartRecommendedReview if (!HasActiveRun() && ActiveReviewPlanState.IsStructurallyValid()) { const FHyperTwistTrainingReviewPlanState StoredReviewPlan = ActiveReviewPlanState; + const FHyperTwistTrainingCoachActionPlan StoredCoachActionPlan = ActiveCoachActionPlan; + const FHyperTwistCoachBrief StoredCoachBrief = ActiveCoachBrief; + const FHyperTwistCoachBrief StoredCoachFollowUpBrief = ActiveCoachFollowUpBrief; const FHyperTwistTrainingDeck ReviewDeck = BuildActiveReviewDeck(MaxCases); if (!ReviewDeck.IsStructurallyValid()) { @@ -3927,6 +4024,45 @@ FHyperTwistTrainingRunState UHyperTwistTrainingSubsystem::StartRecommendedReview ActiveReviewFlowStatus = UHyperTwistTrainingRepositoryLibrary::DeriveReviewFlowStatus( ActiveReviewPlanState ); + if (StoredCoachActionPlan.IsStructurallyValid()) + { + const FHyperTwistCoachBrief CarryoverResumeBrief = + HyperTwistTrainingSubsystemInternal::BuildCarryoverReviewResumeBrief( + StoredReviewPlan, + StoredCoachActionPlan, + StoredCoachBrief, + StoredCoachFollowUpBrief, + ReviewDeck + ); + const TArray LaunchedCaseIds = + HyperTwistTrainingSubsystemInternal::ExtractCaseIdsFromDeck(ReviewDeck); + FHyperTwistTrainingCoachActionPlan DraftActionPlan = + UHyperTwistTrainingRepositoryLibrary::BuildCoachActionPlan( + CarryoverResumeBrief, + ActiveCoachMemorySnapshot, + FString::Printf( + TEXT("coach_carryover_review_%s"), + *RunState.Session.TrainingSessionId + ), + RunState.Session.StartedAtUtc, + ReviewDeck.Cases.Num(), + ReviewDeck.Cases.Num(), + TEXT("resume carryover review plan"), + LaunchedCaseIds + ); + if (DraftActionPlan.IsStructurallyValid()) + { + ActiveCoachActionPlan = UHyperTwistTrainingRepositoryLibrary::ActivateCoachActionPlan( + DraftActionPlan, + RunState.Session.TrainingSessionId, + RunState.Session.StartedAtUtc + ); + TrainingRepositoryState = UHyperTwistTrainingRepositoryLibrary::UpsertCoachActionPlan( + TrainingRepositoryState, + ActiveCoachActionPlan + ); + } + } RefreshRepositoryViews(); return RunState; } diff --git a/docs/HYPERTWIST_ROADMAP_OVERHAUL_EXPANSION_GUIDE.md b/docs/HYPERTWIST_ROADMAP_OVERHAUL_EXPANSION_GUIDE.md index 8103a51..096f84b 100644 --- a/docs/HYPERTWIST_ROADMAP_OVERHAUL_EXPANSION_GUIDE.md +++ b/docs/HYPERTWIST_ROADMAP_OVERHAUL_EXPANSION_GUIDE.md @@ -66,9 +66,9 @@ Current correction call from the source-exposed audit and current execution refr - 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 is closed; request/config/selection plumbing and the bounded clean-room executor are already landed in first-party code - - the latest landed bounded `Phase 4` packet closes the dashboard template-launch analytics refinement lane above the already-landed recognition/review/queue continuity surfaces - - that closure now requires reinforced-vs-plain `DecisionSplitGuard` historical advantage before the final decision-bias snapshot guard can reinforce broader template-selection ties - - the current next bounded packet is the final full recognition-assisted coach-to-review closure validation pass across replay pressure, queue launch, post-clear review rehydration, post-clear action-plan rehydration, post-clear review resumption, final carryover consumption, review-program exhaustion, and truthful idle dashboard closure + - the latest landed bounded `Phase 4` packet closes the carryover-review action-plan resumption gap inside the already-live recognition-assisted coach-to-review continuity lane + - resumed idle carryover review launches now rebuild and activate a fresh coach action plan tied to the resumed session instead of dropping the carried-forward closure lineage at relaunch + - the current next bounded packet is to continue the same recognition-assisted coach-to-review closure validation pass from resumed carryover completion through final carryover consumption, review-program exhaustion, and truthful idle dashboard closure, landing only the next continuity fix that validation exposes - 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 diff --git a/docs/arch/HYPERTWIST_PHASE4_CARRYOVER_REVIEW_ACTION_PLAN_RESUMPTION_CONTINUITY_PACKET_2026-05-07.md b/docs/arch/HYPERTWIST_PHASE4_CARRYOVER_REVIEW_ACTION_PLAN_RESUMPTION_CONTINUITY_PACKET_2026-05-07.md new file mode 100644 index 0000000..16b030e --- /dev/null +++ b/docs/arch/HYPERTWIST_PHASE4_CARRYOVER_REVIEW_ACTION_PLAN_RESUMPTION_CONTINUITY_PACKET_2026-05-07.md @@ -0,0 +1,104 @@ +# HyperTwist Phase 4 carryover review action-plan resumption continuity packet + +Created on `2026-05-07` + +Status: + +- first-party HyperTwist packet +- bounded Phase `4` coach-to-review validation slice + +## Purpose + +This packet preserves coach action-plan continuity when an idle carryover review plan resumes after `ClearActiveRun()`. + +The open tasks are: + +- stop dropping the rehydrated coach action-plan lineage when `StartRecommendedReviewRun()` resumes a stored carryover review plan from idle state +- make the resumed carryover review session own a fresh active coach action plan tied to the resumed session so closure and outcome history advance truthfully + +It is not: + +- a new review-plan persistence packet +- a new queue-state packet +- a broader coach-brief redesign +- a dashboard command-surface rewrite + +## Scope + +Bounded lane: + +- snapshot the stored review plan, coach briefs, and carry-forward action plan before `StartTrainingRunFromDeck()` clears transient coach state +- derive a carryover-review resume brief from the stored review plan, carried-forward action-plan lineage, and resumed review deck +- build, activate, and upsert a new coach action plan for the resumed carryover review session after the run launches +- keep the existing stored review-plan resumption path and true-idle closure behavior intact + +Out of scope: + +- changing review-plan scoring or completion rules +- changing queue execution ordering +- rewriting coach memory derivation +- widening into unrelated template analytics work + +## Why this was the right next packet + +Before this slice: + +- post-clear action-plan rehydration was already landed +- post-clear review-plan resumption was already landed +- the subsystem could inspect and relaunch a stored carryover review plan from idle state + +But one continuity seam still remained: + +- `StartTrainingRunFromDeck()` correctly clears transient coach state before any new run +- the idle carryover review resumption path restored the stored review plan afterward +- but it did not attach a new active coach action plan to the resumed review session + +That meant: + +- carryover review execution could resume correctly +- while the resumed session itself no longer owned the action-plan lineage that downstream closure, outcome, and follow-up reconstruction expect + +So the next honest move was: + +- keep the stored review-plan resumption path +- and immediately rebuild a fresh active coach action plan for the resumed carryover review session from the carried-forward lineage + +## What landed + +Primary code changes: + +- `UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingSubsystem.cpp` + - added `BuildCarryoverReviewResumeBrief(...)` to rebuild a truthful resume brief from stored review-plan state, carried-forward action-plan lineage, and the relaunched review deck + - `StartRecommendedReviewRun()` now snapshots the stored coach briefs and carried-forward action plan before launching the resumed carryover review run + - after launch, it builds `coach_carryover_review_` as a fresh action plan, activates it against the resumed session id, upserts it into repository state, and then refreshes repository views + +## Product effect + +The post-clear carryover lane is now more truthful: + +- resumed carryover review runs keep a live coach action plan instead of silently dropping action-plan lineage at the relaunch boundary +- resumed-session closure and outcome history now attach to the resumed review session rather than relying only on stale pre-resume plan context +- follow-up and idle reconstruction after the resumed review can keep using the correct carry-forward lineage until carryover is genuinely exhausted + +## Acceptance criteria + +- when an idle stored carryover review plan resumes, the subsystem creates a fresh active coach action plan if a carried-forward plan lineage exists +- that resumed action plan preserves parent/root review lineage and review focus context +- repository state stores the resumed action plan before the final repository refresh +- full product build succeeds + +## Validation checklist + +1. build `UnrealHyperTwist.sln` / `UnrealHyperTwistEditor` +2. confirm idle `StartRecommendedReviewRun()` snapshots stored coach action state before `StartTrainingRunFromDeck()` clears it +3. confirm resumed carryover review launch builds and activates `coach_carryover_review_` +4. confirm the resumed action plan preserves carry-forward lineage while using the resumed session id as the active source session +5. confirm no broader queue/dashboard redesign was reopened + +## Validation result + +- full `Development Editor|Win64` build succeeded on `2026-05-07` + +## Next bounded follow-on slice + +- continue the same recognition-assisted coach-to-review closure validation pass from resumed carryover completion through final carryover consumption, review-program exhaustion, and truthful idle dashboard closure, and land only the next continuity fix that pass exposes