diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingSubsystem.cpp b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingSubsystem.cpp index 92adf4b..92bff9b 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingSubsystem.cpp +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingSubsystem.cpp @@ -2291,7 +2291,10 @@ FHyperTwistTrainingCoachPanelState UHyperTwistTrainingSubsystem::GetActiveCoachP bCoachPrefersRecognitionReview ? TEXT("preferred") : TEXT("inactive") ); const bool bHasActiveReviewPlan = ActiveReviewPlanState.IsStructurallyValid(); - const bool bHasReviewPolicyPreview = HasActiveRun(); + const bool bHasReviewPolicyPreview = + HasActiveRun() + && ActiveRunState.Session.SessionState != EHyperTwistTrainingSessionState::Completed + && ActiveRunState.Session.SessionState != EHyperTwistTrainingSessionState::Aborted; FHyperTwistTrainingReviewPolicy DisplayedReviewPolicy; if (bHasActiveReviewPlan) { @@ -5037,6 +5040,9 @@ void UHyperTwistTrainingSubsystem::RefreshRepositoryViews() RepositoryViewUserId, RepositoryViewReferenceUtc ); + const bool bRepositoryHasPendingReviewPlan = + !ActiveReviewProgramSummary.CurrentPlanId.IsEmpty() + || !ActiveReviewProgramSummary.NextPlanId.IsEmpty(); if (!bHasActiveRunContext) { const FString RepositoryReviewPlanId = !ActiveReviewProgramSummary.CurrentPlanId.IsEmpty() @@ -5061,6 +5067,14 @@ void UHyperTwistTrainingSubsystem::RefreshRepositoryViews() ActiveReviewPlanState = FHyperTwistTrainingReviewPlanState(); } } + else if ( + (ActiveRunState.Session.SessionState == EHyperTwistTrainingSessionState::Completed + || ActiveRunState.Session.SessionState == EHyperTwistTrainingSessionState::Aborted) + && ActiveReviewPlanState.IsStructurallyValid() + && !bRepositoryHasPendingReviewPlan) + { + ActiveReviewPlanState = FHyperTwistTrainingReviewPlanState(); + } const FString ResolvedDeckId = bHasActiveRunContext ? ActiveRunState.Session.DeckId diff --git a/docs/arch/HYPERTWIST_PHASE4_COMPLETED_REVIEW_IDLE_CLOSURE_PACKET_2026-05-06.md b/docs/arch/HYPERTWIST_PHASE4_COMPLETED_REVIEW_IDLE_CLOSURE_PACKET_2026-05-06.md new file mode 100644 index 0000000..21b2ee5 --- /dev/null +++ b/docs/arch/HYPERTWIST_PHASE4_COMPLETED_REVIEW_IDLE_CLOSURE_PACKET_2026-05-06.md @@ -0,0 +1,95 @@ +# HyperTwist Phase 4 completed-review idle-closure packet + +Created on `2026-05-06` + +Status: + +- first-party HyperTwist packet +- bounded Phase `4` coach-to-review validation slice + +## Purpose + +This packet closes the remaining stale review-inspection seam at the end of the live recognition-assisted coach-to-review loop. + +The open tasks are: + +- stop treating a completed review plan as an active review plan once repository-backed review-program state has no pending successor +- stop showing a delivery-mode review-policy preview merely because a completed run has not been cleared yet + +It is not: + +- a new review recommendation packet +- a queue-recovery packet +- a dashboard layout redesign +- a recognition transport packet + +## Scope + +Bounded lane: + +- clear `ActiveReviewPlanState` after run completion when repository-backed review-program state no longer exposes `CurrentPlanId` or `NextPlanId` +- limit review-policy preview to genuinely live run states rather than completed/aborted inspection state +- preserve the existing idle review-plan rehydration path when carryover still exists after `ClearActiveRun()` + +Out of scope: + +- new review-policy heuristics +- queue prioritization changes +- broader coach-memory retuning +- structural cleanup outside the review-closure seam + +## Why this was the right next packet + +Before this slice: + +- review finalization preserved carryover instead of force-closing it +- clear-run repository-view continuity was landed +- post-clear queue idle continuity was landed +- idle review-plan continuity was landed + +But one stale boundary still remained: + +- when the last carryover plan was fully consumed, the repository summary could already be idle-clean while the subsystem still held the just-completed `ActiveReviewPlanState` in memory until `ClearActiveRun()` +- review-policy inspection also still allowed delivery-mode preview on a completed run simply because the run had not been cleared yet + +That meant: + +- review inspection could still look active after the review program was already exhausted +- the dashboard could keep showing review-policy materialization at a boundary that should already read as closed review pressure plus completed-run inspection + +So the next honest move was: + +- collapse completed review-plan inspection as soon as repository-backed review-program state confirms there is no pending successor, and suppress review-policy preview on completed/aborted runs + +## What landed + +Primary code changes: + +- `UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingSubsystem.cpp` + - review-policy preview now requires a genuinely live run state, not merely an uncleared completed/aborted run + - repository refresh now clears `ActiveReviewPlanState` after run completion when the review-program summary has no `CurrentPlanId` or `NextPlanId` + +## Product effect + +The final review boundary is now more truthful: + +- completed review plans no longer linger as active review inspection after the last carryover work is already exhausted +- dashboard review-policy inspection stops showing stale preview state on completed runs +- carryover that still exists after `ClearActiveRun()` remains rehydratable through the already-landed idle review-plan continuity path + +## Acceptance criteria + +- completed/aborted runs do not show review-policy preview solely because they remain uncleared +- when repository-backed review-program state has no pending successor, completed review-plan state is cleared from runtime inspection +- carryover-backed pending review plans still rehydrate correctly after `ClearActiveRun()` +- full product build succeeds + +## Validation checklist + +1. build `UnrealHyperTwist.sln` / `UnrealHyperTwistEditor` +2. confirm completed runs do not surface delivery-mode review-policy preview +3. confirm exhausted review programs clear runtime active-review-plan inspection before `ClearActiveRun()` +4. confirm idle review-plan rehydration still works when carryover remains open +5. confirm no queue or review-policy redesign was reopened + +That is the packet.